Наша взаимовыгодная связь https://banwar.org/
Про що це ми тут?
У цьому проекті я хотів би розповісти про те, як можна зробити красиве хмара тегів на Silverlight.
Відразу хочу зізнатися, що ідею і частина реалізації я підглянув у інших. Однак, наведений там приклад злегка глючил. Тому я вирішив написати цю статтю.
Поставимо собі задачу в такий спосіб:
- відображення тегів на поверхні 3D сфери (або еліпсоїда);
- обертання хмари в залежності від положення миші;
- отримання інформації про тегах в XML документі.
Вибір 3D бібліотеки:
На жаль, остання версія Silverlight не включає в себе тривимірні можливості як в WPF. На щастя, частина необхідного функціоналу написана сторонніми розробниками. В даному проекті буде використовується бібліотека Axelerate3D .
XAML
<UserControl x: Class = "Mercury.Web.Silverlight.Tags.MainPage" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns: x = "http://schemas.microsoft .com / winfx / 2006 / xaml "HorizontalAlignment =" Stretch "VerticalAlignment =" Stretch "> <Grid x: Name =" LayoutRoot "Background =" White "> <Canvas x: Name =" RootCanvas "HorizontalAlignment =" Stretch "VerticalAlignment = "Stretch"> </ Canvas> </ Grid> </ UserControl>Тут наведено XAML-код основного класу Silverlight додатки (MainPage). Як бачите, тут все просто. Один єдиний Canvas, який буде займатися рендерингом хмари.
Показ тегу: Перейдемо до створення класу роботи з тегом. Він (клас) буде представляти мітку з потрібним текстом, яка буде розташована в координатах (x, y, z) щодо центру хмари.
public class Tag3D {public Tag3D (double x, double y, double z, string text, Uri uri) {CenterPoint = new Point3D (x, y, z); TextBlock = new TextBlock {Text = text}; BtnLink = new HyperlinkButton {Content = TextBlock, BorderThickness = new Thickness (0), Padding = new Thickness (0), NavigateUri = uri, Visibility = Visibility.Collapsed}; } Public HyperlinkButton BtnLink {get; set; } Public Point3D CenterPoint {get; set; } Private TextBlock TextBlock {get; set; } Public void Redraw (UISize size) {...} private void UpdatePosition (double rx, double ry, double xOffset, double yOffset) {...} private void UpdateLayout () {...} private void UpdateSize () { ...} private void UpdateColor () {...} private void UpdateVisibility () {...}}Потім нам необхідно зробити методи для визначення розмірів і кольору обраного тега:
private void UpdateSize () {BtnLink.FontSize = 16 + CenterPoint.Z * 4; } Private void UpdateColor () {var color = 160 + CenterPoint.Z * 96; color = Math.Max (0, color); color = Math.Min (255, color); BtnLink.Foreground = new SolidColorBrush (Color.FromArgb (Convert.ToByte (color), 0, 0, 0)); }Тепер позиціонуємо тег в просторі:
// rx, ry - розміри еліпса хмари (в проекції на площину) // xOffset, yOffset - координати центру хмари, щодо Canvas private void UpdatePosition (double rx, double ry, double xOffset, double yOffset) {var maxZ = Math.Min (rx, ry); var x = (xOffset + CenterPoint.X * rx) - (BtnLink.ActualWidth / 2.0); var y = (yOffset - CenterPoint.Y * ry) - (BtnLink.ActualHeight / 2.0); var z = CenterPoint.Z * maxZ; Canvas.SetLeft (BtnLink, x); Canvas.SetTop (BtnLink, y); Canvas.SetZIndex (BtnLink, Convert.ToInt32 (z)); }Розміщення тегів на поверхні сфери: Повернемося до класу MainPage.
Для розуміння наступного фрагмента коду необхідно деяке знання математики. Цей метод розподіляє теги по поверхні сфери.
private void FillTags () {RootCanvas.UpdateLayout (); RootCanvas.Children.Clear (); tagBlocks = new List <Tag3D> (); var length = tagList.Count; for (var i = 1; i <= length; i ++) {var phi = Math.Acos (-1.0 + (2.0 * i - 1.0) / length); var theta = Math.Sqrt (length Math.PI) phi; var x = Math.Cos (theta) * Math.Sin (phi); var y = Math.Sin (theta) * Math.Sin (phi); var z = Math.Cos (phi); var tag = tagList [i - 1]; var uri = UrlHelper.GetTagUri (UrlEncode (tag)); var item = new Tag3D (x, y, z, tag, uri); RootCanvas.Children.Add (item.BtnLink); tagBlocks.Add (item); }}Обертання хмари: Для обертання хмари ми будемо використовувати положення курсора миші. Чим більше відстань від центру хмари, до курсора, тим більше кут повороту хмари, а значить і швидкість його обертання.
У момент ініціалізації надаємо невелику початкове обертання:
private readonly RotateTransform3D rotateTransform = new RotateTransform3D (); public void Run () {CompositionTarget.Rendering + = OnCompositionTargetRendering; LayoutRoot.MouseEnter + = OnLayoutRootMouseEnter; LayoutRoot.MouseLeave + = OnLayoutRootMouseLeave; slowDownCounter = 500.0; runRotation = true; rotateTransform.Rotation = new AxisAngleRotation3D (new Vector3D (0.8, 0.6, 0), 0.5); FillTags (); }Визначаємо напрямок і швидкість обертання хмари:
private void OnLayoutRootMouseMove (object sender, MouseEventArgs e) {var position = e.GetPosition (RootCanvas); SetRotateTransform (position); } Private void SetRotateTransform (Point position) {var size = GetUISizes (); var x = (position.X - size.XOffset) / size.XRadius; var y = (position.Y - size.YOffset) / size.YRadius; var angle = Math.Sqrt (xx + yy); rotateTransform.Rotation = new AxisAngleRotation3D (new Vector3D (-y, -x, 0.0), angle); } Private UISize GetUISizes () {return new UISize {XOffset = RootCanvas.ActualWidth / 2.0, YOffset = RootCanvas.ActualHeight / 2.0}; }Тепер займемося отрисовкой тегів.
private void OnCompositionTargetRendering (object sender, EventArgs e) {if (! (runRotation || (slowDownCounter <= 0.0))) {var rotation = (AxisAngleRotation3D) rotateTransform.Rotation; rotation.Angle * = slowDownCounter / 500.0; rotateTransform.Rotation = rotation; slowDownCounter--; } If (((AxisAngleRotation3D) rotateTransform.Rotation) .Angle> 0.05) {RotateBlocks (); }} Private void RotateBlocks () {var size = GetUISizes (); foreach (var tagd in tagBlocks) {Point3D pointd; if (rotateTransform.TryTransform (tagd.CenterPoint, out pointd)) {tagd.CenterPoint = pointd; tagd.Redraw (size); }}}Завантаження списку ключових слів:
Тепер можна подбати про те, щоб наповнити наше хмари даними про тегах.
Шлях деякий Url повертає нам XML ось такої структури:
<? Xml version = "1.0" encoding = "utf-8"?> <Taglist> <tag> IIS7 </ tag> <tag> .NET </ tag> <tag> Silverlihgt </ tag> <tag> 3D < / tag> </ taglist>Тоді код для отримання списку тегів може виглядати так:
private void LoadTagList (Uri uri) {try {// Отримуємо XML зі списком тегів. var request = (HttpWebRequest) WebRequest.Create (uri); var waitingEvent = new AutoResetEvent (false); var callback = new AsyncCallback (result => ((EventWaitHandle) result.AsyncState) .Set ()); var asyncResult = request.BeginGetResponse (callback, waitingEvent); waitingEvent.WaitOne (); var response = request.EndGetResponse (asyncResult); // Парс XML і складаємо список тегів. var doc = XDocument.Load (response.GetResponseStream ()); tagList.AddRange (doc.Descendants ( "tag"). Select (item => item.Value)); Dispatcher.BeginInvoke (Run); } Catch {}}Усе!
Вихідні тексти можна завантажити тут .
HTML код:
<Object width = "500" height = "300" type = "application / x-silverlight-2" data = "data: application / x-silverlight-2,"> <param name = "source" value = "/ ClientBin /Mercury.Web.Silverlight.Tags.xap "/> <param name =" background "value =" white "/> <param name =" minRuntimeVersion "value =" 3.0.40624.0 "/> <param name =" autoUpgrade " value = "true" /> <param name = "windowless" value = "true" /> <param name = "enableGPUAcceleration" value = "True" /> <param name = "initParams" value = "url = / Node / Tags / {0}, service = / Node / TagList "/> <a style =" text-decoration: none "href =" http://go.microsoft.com/fwlink/?LinkID=149156&amp;v= 3.0.40624.0 "> <img style =" border-style: none "src =" http://go.microsoft.com/fwlink/?LinkId=108181 "alt =" Get Microsoft Silverlight "/> </a> < / object>От і все!
Про що це ми тут?Quot; encoding = "utf-8"?
Com/fwlink/?
Com/fwlink/?