通通玩blend美工(8)——动态绘制路径动画,画出个萌妹子~
?
?
2年前我在玩Flex的時候就一直有一個疑問,就是如何來實現一個蚊香慢慢燒完的Loading動畫呢?
剛經歷了某甲方高強度一個月的洗禮后,這幾天剛好閑下來,這個問題又浮現在我腦海里。于是經過幾番思索糾結后,我發現了一個更好玩的效果,如下:
? ? ? ? ? ? ? ?
↑點擊開始 只要是通過path來表示的都可以畫出來哦親~如果好好構思一下是可以實現很震撼的效果的,你想到了沒?? ?
1.總體思路
下面我就來分析一下實現思路。
仔細觀察下,
1:遍歷出所有的Path。
2:把各個Path分解成各種點。
3:依次對各個點執行PointAnimation動畫(直線執行PointAnimation,曲線執行PointAnimationUsingPath)。
?
?
2.詳細設計
? 首先,我們得畫一個路徑作為動畫的樣本路徑。
?1、準備萌妹子一枚
? ? ? ? ? ? ? ? ? ? ?
?
用打開后,轉成路徑,如下:
參數:
確定后得到
可以手動刪除一些無用的路徑。
然后導出路徑
? ? ? ? ? ? ??
打開導出的Xaml將Path都Copy出來用。
?
?
2、遍歷出萌妹子的各種輪廓
這里我之前寫過一個遍歷一個對象的可視樹下所有的某類型的子對象的方法(wpf移植過來的為了適應,臨時小改了一下)。
List<Path> list = new List<Path>();/// <summary>/// 遍歷某對象下某類型的所有子元素/// </summary>/// <param name="myVisual">遍歷的對象</param>/// <param name="type">子元素的類型</param>public void EnumVisual(DependencyObject myVisual, Type type){for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++){DependencyObject childVisual = (DependencyObject)VisualTreeHelper.GetChild(myVisual, i);if (childVisual != null){if (childVisual.GetType() == type){list.Add(childVisual as Path);}EnumVisual(childVisual, type);}}return;}然后
EnumVisual(Group, typeof(Path));其中Group為包含所有Path的容器,這樣list里就充滿了萌妹子的輪廓。
?
3、把每一條Path都分割成各種點的表示方式
這里我們先來了解一下Path的路徑標記語法。其實,Path的線段是有兩種表示方法的,如下:
第一種:
<Path Data="M96,200 L176,288 C176,288 272,232 248,208" Fill="#FFF8F8F8" Stroke="Black"/>第二種:
?
<Path x:Name="path" Fill="#FFF8F8F8" Stroke="Black"><Path.Data><PathGeometry><PathFigure StartPoint="96,200"><LineSegment Point="176,288"/><BezierSegment Point3="248,208" Point2="272,232" Point1="176,288"/></PathFigure></PathGeometry></Path.Data></Path>?
這兩段代碼表示的是同樣的路徑。其中M=StartPoint,L=LineSegment,C=BezierSegment,LineSegment的Point屬性指的是線段的終結點,BezierSegment的Point3指的是曲線的終結點,Point1和Point2分別指的是起始點控制點和結束點控制點的位置,就是用鋼筆工具畫曲線時拖動曲度的那兩個點。當然了,每個線段的起始點為上一個線段的終結點。迷你表示法還有大小寫分別表示絕對位置與相對位置,更多說明請參考下面鏈接。
詳情請參考:http://msdn.microsoft.com/zh-cn/library/ms752293.aspx
我們遍歷出來的Path其實都是用第一種表示方法表示的,這樣不方便我們后臺來分解解析。我們得把第一種表示方法裝換為第二種。這里我們得借用國外大俠分享的類:
http://stringtopathgeometry.codeplex.com/
這個東西可以很方便的實現這兩種格式的轉換,如下:
?
StringToPathGeometryConverter _s = new StringToPathGeometryConverter(); PathGeometry PG = _s.Convert(path.Data.ToString());?
這樣轉換出來的PG就是第二種表示方法了。
通過遍歷第二種表示方法里面PathFigure的子項就可以知道點與點之間的關系了,依次在點與點之間執行PointAnimation和PointAnimationUsingPath讓終結點從起始點運動到結束位置,就實現劃線的效果了。
? 曲線的話必須得用PointAnimationUsingPath來執行才顯得自然一些,當然了,silverlight里是沒有PointAnimationUsingPath動畫的,所以得借助某外國達人寫的PointAnimationUsingPath類,用法和WPF里差不多,不過個人覺得可以在Animation里直接來設置target和targetProperty還有Begin方法,這一點比微軟寫的更好用。
詳情請參看:http://www.codeproject.com/Articles/30819/Animation-Along-a-Path-for-Silverlight
好了,直接上代碼:
?
全局變量 /// <summary>/// 存儲遍歷到的Path樣本/// </summary>List<Path> list = new List<Path>();/// <summary>/// Path樣本索引/// </summary>int pathNum = 0;/// <summary>/// 新繪制的路徑的集合/// </summary>List<Path> drawPathList = new List<Path>();/// <summary>/// 點集合索引/// </summary>int num = 0;/// <summary>/// 當前繪制路徑的樣本Path /// </summary> PathGeometry PG;/// <summary>/// 當前繪制路徑中的點集合 /// </summary> PathFigure PF;/// <summary>/// 當前繪制路徑中的結束點/// </summary>Point endPoint;?
畫線方法 /// <summary>/// 開始繪制一條路徑/// </summary>/// <param name="path">路徑樣本</param>private void PathPlay(Path path){Path ph = new Path();//當前繪制的路徑 drawPathList.Add(ph);ph.Stroke = new SolidColorBrush(Colors.Black);drawGrid.Children.Add(ph);//添加進畫布 PathGeometry thisPG = new PathGeometry();//動畫路徑的數據ph.Data = thisPG;StringToPathGeometryConverter _s = new StringToPathGeometryConverter();PG = _s.Convert(path.Data.ToString());//讀取樣本路徑,分解段PF = new PathFigure();//創建集合endPoint = PF.StartPoint = PG.Figures[0].StartPoint;//設置起始點,一開始的終結點為起始點 thisPG.Figures.Add(PF);Play();//開始繪制分段的路徑 }/// <summary>/// 繪制路徑的一段/// </summary>private void Play(){try{if (pathNum >= list.Count)//畫完所有的路徑 {FillColor();//開始填充顏色Group.Visibility = Visibility.Visible; return;}else if (num >= PG.Figures[0].Segments.Count)//畫完一條線 {if (pathNum < list.Count){num = 0;PathPlay(list[pathNum++] as Path);//播放完畢就播放下一條線 return;}}PathSegment item = PG.Figures[0].Segments[num++];//讀取下一個點if (item.ToString().Contains("Line"))//如果這個點是直線 {LineSegment _ls = new LineSegment();_ls.Point = endPoint; PathSegment PS = _ls;//創建一條直線的初始狀態點 PointAnimation PA = new PointAnimation();//動畫到讀取的點的位置PA.To=(item as LineSegment).Point;PA.Duration = new Duration(TimeSpan.FromMilliseconds(50));PA.Completed += new EventHandler((sender1, e1) =>//播放完畢后進行遞歸,繪制下一條線 {Play();});PF.Segments.Add(PS);//添加點//PS.BeginAnimation(LineSegment.PointProperty, PA);Storyboard sb = new Storyboard();sb.Children.Add(PA);Storyboard.SetTarget(PA, PS);Storyboard.SetTargetProperty(PA, new PropertyPath("Point"));sb.Begin();//開始動畫endPoint = (item as LineSegment).Point;//記錄終結點 }else if (item.ToString().Contains("Bezier"))//如果這個點是貝爾曲線 {BezierSegment _bs = new BezierSegment();_bs.Point1 = _bs.Point2 = _bs.Point3 = endPoint;PathSegment PS = _bs;PointAnimationUsingPath PA = new PointAnimationUsingPath();//創建終結點的路徑動畫,曲線要嚴格按照路徑來運動PA.Target = PS;PA.TargetProperty = new PropertyPath("Point3");PA.Duration = TimeSpan.FromMilliseconds(50);//生成動畫的路徑形狀PathGeometry newPG = new PathGeometry();PathFigure newPF = new PathFigure();//創建集合newPF.StartPoint = endPoint;//s設置起始點和每次動畫的種植點 newPG.Figures.Add(newPF);BezierSegment _bs1 = new BezierSegment();_bs1.Point1 = (item as BezierSegment).Point1;_bs1.Point2 = (item as BezierSegment).Point2;_bs1.Point3 = (item as BezierSegment).Point3;newPF.Segments.Add(_bs1);PA.PathGeometry = newPG;PA.Completed += new EventHandler((sender1, e1) =>{Play();});PA.Begin();//同樣對控制點也要進行一般的動畫PointAnimation PA1 = new PointAnimation();PA1.To=(item as BezierSegment).Point1;PA1.Duration=new Duration(TimeSpan.FromMilliseconds(500));PointAnimation PA2 = new PointAnimation();PA2.To=(item as BezierSegment).Point2;PA2.Duration=new Duration(TimeSpan.FromMilliseconds(500));PF.Segments.Add(PS);//PS.BeginAnimation(BezierSegment.Point3Property, PA);//PS.BeginAnimation(BezierSegment.Point1Property, PA1);//PS.BeginAnimation(BezierSegment.Point2Property, PA2);Storyboard sb = new Storyboard();//sb.Children.Add(PA);//Storyboard.SetTarget(PA, PS);//Storyboard.SetTargetProperty(PA, new PropertyPath("Point"));//sb.Begin();//開始動畫endPoint = (item as BezierSegment).Point3;}}catch{}}方法里用了各種遞歸是因為處理完一條Path的所有動畫后執行下一條Path的動畫,而每一條Path里的每一小段也得依次處理, 要讓一序列的動畫依次播放,得在動畫播放完畢后再播放下一段動畫,各位大蝦有沒有更好的方法來依次播放一序列動畫呢??
?
?
后記
原版是Wpf的,wpf自帶了PointAnimationUsingPath動畫,所以實現起來代碼少得多了。接下來我打算優化后把它封成一個行為,方便以后使用。
文中出現了這么多外國牛人的文章,當然了以小弟的強烈愛國情懷是無法完全理解,所以特別謝http://www.cnblogs.com/beniao/archive/2010/05/26/1736446.html。
覺得本文還可以的話要點擊下面的推薦哦喵~
?
?
?
?
posted on 2018-08-03 00:26 NET未來之路 閱讀(...) 評論(...) 編輯 收藏
轉載于:https://www.cnblogs.com/lonelyxmas/p/9411026.html
總結
以上是生活随笔為你收集整理的通通玩blend美工(8)——动态绘制路径动画,画出个萌妹子~的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Codeforces 990E Post
- 下一篇: java基础基础总结----- Date