[UWP]用Shape做动画(2):使用与扩展PointAnimation
上一篇幾乎都在說(shuō)DoubleAnimation的應(yīng)用,這篇說(shuō)說(shuō)PointAnimation。
1. 使用PointAnimation
使用PointAnimation可以讓Shape變形,但實(shí)際上沒(méi)看到多少人會(huì)這么用,畢竟WPF做的軟件多數(shù)不需要這么花俏。
1.1 在XAML上使用PointAnimation
<Storyboard?x:Name="Storyboard2"RepeatBehavior="Forever"AutoReverse="True"Duration="0:0:4"><PointAnimation?Storyboard.TargetProperty="(Path.Data).(PathGeometry.Figures)[0].(PathFigure.StartPoint)"Storyboard.TargetName="Path2"To="0,0"EnableDependentAnimation="True"?/><PointAnimation?Storyboard.TargetProperty="(Path.Data).(PathGeometry.Figures)[0].(PathFigure.Segments)[0].(LineSegment.Point)"Storyboard.TargetName="Path2"To="100,0"EnableDependentAnimation="True"?/><ColorAnimation?To="#FF85C82E"Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"Storyboard.TargetName="Path2"?/> </Storyboard>…<Path?Margin="0,20,0,0"x:Name="Path2"Fill="GreenYellow"><Path.Data><PathGeometry><PathFigure?StartPoint="50,0"><LineSegment?Point="50,0"?/><LineSegment?Point="0,100"?/><LineSegment?Point="0,100"?/><LineSegment?Point="100,100"?/><LineSegment?Point="100,100"?/></PathFigure></PathGeometry></Path.Data> </Path>在這個(gè)例子里最頭痛的地方是Property-path 語(yǔ)法,如果不能熟記的話最好依賴Blend生成。
1.2 在代碼中使用PointAnimation
如果Point數(shù)量很多,例如圖表,通常會(huì)在C#代碼中使用PointAnimation:
_storyboard?=?new?Storyboard(); Random?random?=?new?Random();for?(int?i?=?0;?i?<?_pathFigure.Segments.Count;?i++) {var?animation?=?new?PointAnimation?{?Duration?=?TimeSpan.FromSeconds(3)?};Storyboard.SetTarget(animation,?_pathFigure.Segments[i]);Storyboard.SetTargetProperty(animation,?"(LineSegment.Point)");animation.EnableDependentAnimation?=?true;animation.EasingFunction?=?new?QuarticEase?{?EasingMode?=?EasingMode.EaseOut?};animation.To?=?new?Point((_pathFigure.Segments[i]?as?LineSegment).Point.X,?(i?%?2?==?0???1?:?-1)?*?i?*?1.2?+?60);_storyboard.Children.Add(animation); } _storyboard.Begin();因?yàn)榭梢灾苯覵etTarget,所以Property-path語(yǔ)法就可以很簡(jiǎn)單。
2. 擴(kuò)展PointAnimation
上面兩個(gè)例子的動(dòng)畫都還算簡(jiǎn)單,如果更復(fù)雜些,XAML或C#代碼都需要寫到很復(fù)雜。我參考了這個(gè)網(wǎng)頁(yè)?想做出類似的動(dòng)畫,但發(fā)現(xiàn)需要寫很多XAML所以放棄用PointAnimation實(shí)現(xiàn)。這個(gè)頁(yè)面的動(dòng)畫核心是這段HTML:
<polygon?fill="#FFD41D"?points="97.3,0?127.4,60.9?194.6,70.7?145.9,118.1?157.4,185.1?97.3,153.5?37.2,185.1?48.6,118.1?0,70.7?67.2,60.9"><animate?id="animation-to-check"?begin="indefinite"?fill="freeze"?attributeName="points"?dur="500ms"?to="110,58.2?147.3,0?192.1,29?141.7,105.1?118.7,139.8?88.8,185.1?46.1,156.5?0,125?23.5,86.6?71.1,116.7"/><animate?id="animation-to-star"?begin="indefinite"?fill="freeze"?attributeName="points"?dur="500ms"?to="97.3,0?127.4,60.9?194.6,70.7?145.9,118.1?157.4,185.1?97.3,153.5?37.2,185.1?48.6,118.1?0,70.7?67.2,60.9"/></polygon>只需一組Point的集合就可以控制所有Point的動(dòng)畫,確實(shí)比PointAnimation高效很多。 在WPF中可以通過(guò)繼承Timeline實(shí)現(xiàn)一個(gè)PointCollectionAnimamtion,具體可以參考這個(gè)項(xiàng)目??上У氖请m然UWP的Timeline類并不封閉,但完全不知道如何繼承并派生一個(gè)自定義的Animation。
這時(shí)候需要稍微變通一下思維??梢詫oubleAnimation理解成這樣:Storyboard將TimeSpan傳遞給DoubleAnimation,DoubleAnimation通過(guò)這個(gè)TimeSpan(有時(shí)還需要結(jié)合EasingFunction)計(jì)算出目標(biāo)屬性的當(dāng)前值最后傳遞給目標(biāo)屬性,如下圖所示:
既然這樣,也可以接收到這個(gè)計(jì)算出來(lái)的Double,再通過(guò)Converter計(jì)算出目標(biāo)的PointCollection值:
假設(shè)告訴這個(gè)Converter當(dāng)傳入的Double值(命名為Progress)為0的時(shí)候,PointCollection是{0,0 1,1 …},Progress為100時(shí)PointCollection是{1,1 2,2 …},當(dāng)Progress處于其中任何值時(shí)的計(jì)算方法則是:
private?PointCollection?GetCurrentPoints(PointCollection?fromPoints,?PointCollection?toPoints,?double?percentage) {var?result?=?new?PointCollection();for?(var?i?=?0;i?<?Math.Min(fromPoints.Count,?toPoints.Count);i++){var?x?=?(1?-?percentage?/?100d)?*?fromPoints[i].X?+?percentage?/?100d?*?toPoints[i].X;var?y?=?(1?-?percentage?/?100d)?*?fromPoints[i].Y?+?percentage?/?100d?*?toPoints[i].Y;result.Add(new?Point(x,?y));}return?result; }這樣就完成了從TimeSpan到PointCollection的轉(zhuǎn)換過(guò)程。然后就是定義在XAML上的使用方式。參考上面PointCollectionAnimation,雖然多了個(gè)Converter,但XAML也應(yīng)該足夠簡(jiǎn)潔:
<local:ProgressToPointCollectionBridge?x:Name="ProgressToPointCollectionBridge"><PointCollection>97.3,0?127.4,60.9?194.6,70.7?145.9,118.1?157.4,185.1?97.3,153.5?37.2,185.1?48.6,118.1?0,70.7?67.2,60.9</PointCollection><PointCollection>110,58.2?147.3,0?192.1,29?141.7,105.1?118.7,139.8?88.8,185.1?46.1,156.5?0,125?23.5,86.6?71.1,116.7</PointCollection> </local:ProgressToPointCollectionBridge><Storyboard?x:Name="Storyboard1"FillBehavior="HoldEnd"><DoubleAnimation?Duration="0:0:2"To="100"FillBehavior="HoldEnd"Storyboard.TargetProperty="(local:ProgressToPointCollectionBridge.Progress)"Storyboard.TargetName="ProgressToPointCollectionBridge"EnableDependentAnimation="True"/> </Storyboard>…<Polygon?x:Name="polygon"Points="{Binding?Source={StaticResource?ProgressToPointCollectionBridge},Path=Points}"Stroke="DarkOliveGreen"StrokeThickness="2"Height="250"Width="250"Stretch="Fill"?/>最終我選擇了將這個(gè)Converter命名為ProgressToPointCollectionBridge??梢钥闯鯬olygon 將Points綁定到ProgressToPointCollectionBridge,DoubleAnimation 改變ProgressToPointCollectionBridge.Progress,從而改變Points。XAML的簡(jiǎn)潔程度還算令人滿意,如果需要操作多個(gè)點(diǎn)的話相對(duì)于PointAnimation的優(yōu)勢(shì)就很大。
運(yùn)行結(jié)果如下:
完整的XAML:
<UserControl.Resources><local:ProgressToPointCollectionBridge?x:Name="ProgressToPointCollectionBridge"><PointCollection>97.3,0?127.4,60.9?194.6,70.7?145.9,118.1?157.4,185.1?97.3,153.5?37.2,185.1?48.6,118.1?0,70.7?67.2,60.9</PointCollection><PointCollection>110,58.2?147.3,0?192.1,29?141.7,105.1?118.7,139.8?88.8,185.1?46.1,156.5?0,125?23.5,86.6?71.1,116.7</PointCollection></local:ProgressToPointCollectionBridge><Storyboard?x:Name="Storyboard1"FillBehavior="HoldEnd"><DoubleAnimation?Duration="0:0:2"To="100"FillBehavior="HoldEnd"Storyboard.TargetProperty="(local:ProgressToPointCollectionBridge.Progress)"Storyboard.TargetName="ProgressToPointCollectionBridge"EnableDependentAnimation="True"><DoubleAnimation.EasingFunction><ElasticEase?EasingMode="EaseInOut"?/></DoubleAnimation.EasingFunction></DoubleAnimation><ColorAnimation?Duration="0:0:2"To="#FF48F412"Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"Storyboard.TargetName="polygon"d:IsOptimized="True"><ColorAnimation.EasingFunction><ElasticEase?EasingMode="EaseInOut"?/></ColorAnimation.EasingFunction></ColorAnimation></Storyboard> </UserControl.Resources> <Grid?x:Name="LayoutRoot"Background="White"><Polygon?x:Name="polygon"Points="{Binding?Source={StaticResource?ProgressToPointCollectionBridge},Path=Points}"Stroke="DarkOliveGreen"StrokeThickness="2"Height="250"Width="250"Stretch="Fill"Fill="#FFEBF412"?/> </Grid>ProgressToPointCollectionBridge:
[ContentProperty(Name?=?nameof(Children))] public?class?ProgressToPointCollectionBridge?:?DependencyObject {public?ProgressToPointCollectionBridge(){Children?=?new?ObservableCollection<PointCollection>();}///?<summary>///?????獲取或設(shè)置Points的值///?</summary>public?PointCollection?Points{get?{?return?(PointCollection)?GetValue(PointsProperty);?}set?{?SetValue(PointsProperty,?value);?}}///?<summary>///?????獲取或設(shè)置Progress的值///?</summary>public?double?Progress{get?{?return?(double)?GetValue(ProgressProperty);?}set?{?SetValue(ProgressProperty,?value);?}}///?<summary>///?????獲取或設(shè)置Children的值///?</summary>public?Collection<PointCollection>?Children{get?{?return?(Collection<PointCollection>)?GetValue(ChildrenProperty);?}set?{?SetValue(ChildrenProperty,?value);?}}protected?virtual?void?OnProgressChanged(double?oldValue,?double?newValue){UpdatePoints();}protected?virtual?void?OnChildrenChanged(Collection<PointCollection>?oldValue,?Collection<PointCollection>?newValue){var?oldCollection?=?oldValue?as?INotifyCollectionChanged;if?(oldCollection?!=?null)oldCollection.CollectionChanged?-=?OnChildrenCollectionChanged;var?newCollection?=?newValue?as?INotifyCollectionChanged;if?(newCollection?!=?null)newCollection.CollectionChanged?+=?OnChildrenCollectionChanged;UpdatePoints();}private?void?OnChildrenCollectionChanged(object?sender,?NotifyCollectionChangedEventArgs?e){UpdatePoints();}private?void?UpdatePoints(){if?(Children?==?null?||?Children.Any()?==?false){Points?=?null;}else?if?(Children.Count?==?1){var?fromPoints?=?new?PointCollection();for?(var?i?=?0;?i?<?Children[0].Count;?i++)fromPoints.Add(new?Point(0,?0));var?toPoints?=?Children[0];Points?=?GetCurrentPoints(fromPoints,?toPoints,?Progress);}else{var?rangePerSection?=?100d?/?(Children.Count?-?1);var?fromIndex?=?Math.Min(Children.Count?-?2,?Convert.ToInt32(Math.Floor(Progress?/?rangePerSection)));fromIndex?=?Math.Max(fromIndex,?0);var?toIndex?=?fromIndex?+?1;PointCollection?fromPoints;if?(fromIndex?==?toIndex){fromPoints?=?new?PointCollection();for?(var?i?=?0;?i?<?Children[0].Count;?i++)fromPoints.Add(new?Point(0,?0));}else{fromPoints?=?Children.ElementAt(fromIndex);}var?toPoints?=?Children.ElementAt(toIndex);var?percentage?=?(Progress?/?rangePerSection?-?fromIndex)?*?100;Points?=?GetCurrentPoints(fromPoints,?toPoints,?percentage);}}private?PointCollection?GetCurrentPoints(PointCollection?fromPoints,?PointCollection?toPoints,?double?percentage){var?result?=?new?PointCollection();for?(var?i?=?0;i?<?Math.Min(fromPoints.Count,?toPoints.Count);i++){var?x?=?(1?-?percentage?/?100d)?*?fromPoints[i].X?+?percentage?/?100d?*?toPoints[i].X;var?y?=?(1?-?percentage?/?100d)?*?fromPoints[i].Y?+?percentage?/?100d?*?toPoints[i].Y;result.Add(new?Point(x,?y));}return?result;}#region?DependencyProperties#endregion }3. 結(jié)語(yǔ)
如果將DoubleAnimation說(shuō)成“對(duì)目標(biāo)的Double屬性做動(dòng)畫”,那PointAnimation可以說(shuō)成“對(duì)目標(biāo)的Point.X和Point.Y兩個(gè)Double屬性同時(shí)做動(dòng)畫”,ColorAnimation則是“對(duì)目標(biāo)的Color.A、R、G、B四個(gè)Int屬性同時(shí)做動(dòng)畫”。這樣理解的話PointAnimation和ColorAnimation只不過(guò)是DoubleAnimation的延伸而已,進(jìn)一步的說(shuō),通過(guò)DoubleAnimation應(yīng)該可以延伸出所有類型屬性的動(dòng)畫。不過(guò)我并不清楚怎么在UWP上自定義動(dòng)畫,只能通過(guò)本文的折衷方式擴(kuò)展。雖然XAML需要寫復(fù)雜些,但這樣也有它的好處:
不需要了解太多Animation相關(guān)類的知識(shí),只需要有依賴屬性、綁定等基礎(chǔ)知識(shí)就夠了。
不會(huì)因?yàn)閯?dòng)畫API的改變而更改,可以兼容WPF、Silverlight和UWP(大概吧,我沒(méi)有真的在WPF上測(cè)試這些代碼)。
代碼足夠簡(jiǎn)單,省去了計(jì)算TimeSpan及EasingFunction的步驟。 稍微修改下還可以做成泛型的AnimationBridge < T >,提供PointCollection以外數(shù)據(jù)類型的支持。
結(jié)合上一篇文章再發(fā)散一下,總覺(jué)得將來(lái)遇到什么UWP沒(méi)有提供的功能都可以通過(guò)變通的方法實(shí)現(xiàn),Binding和DependencyProperty真是UWP開發(fā)者最好的朋友。
轉(zhuǎn)載于:https://blog.51cto.com/yexuanke/1942112
總結(jié)
以上是生活随笔為你收集整理的[UWP]用Shape做动画(2):使用与扩展PointAnimation的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 手工安装kubernetes
- 下一篇: day26 re正则表达式