The previous article almost all said DoubleAnimation application, this article said PointAnimation.
1. Using PointAnimation
Using PointAnimation can make shape deform, but you don't actually see how many people use it, and most of the software that WPF does doesn't need to be so fancy.
1.1 Using PointAnimation on XAML
<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 "E nabledependentanimation= "True"/><pointanimation storyboard.targetproperty= "(path.data). (PathGeometry.Figures) [0]. (pathfigure.segments) [0]. (linesegment.point) "Storyboard.targetname=" Path2 "to=" 100,0 "Ena bledependentanimation= "True"/><coloranimation to= "#FF85C82E" storyboard.targetproperty= "(shape.f ILL). (solidcolorbrush.color) "Storyboard.targetname=" Path2 "/></storyboard>...<path Margin=" 0,20,0 , 0 "x:name=" Path2 "fill=" Greenyellow "><path.data><pathgeometry><pathfigure StartPoint=" 50,0 "&G T;<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>
The most headache in this case is the Property-path syntax, which is best relied on in blend if it cannot be memorized.
1.2 Using PointAnimation in your code
If there is a large number of point numbers, the example table will typically use PointAnimation in C # code:
_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 +); _storyboard. Children.add (animation);} _storyboard. Begin ();
Because it can be direct SetTarget
, so the Property-path syntax can be very simple.
2. Extended PointAnimation
The animations for the two examples above are fairly simple, and if they are more complex, the XAML or C # code needs to be written to be complex. I refer to this page to make a similar animation, but I find that I need to write a lot of XAML so I discard the pointanimation implementation. The animation core of this page is this 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>
The ability to control all of the point animations with just a set of point sets is really much more efficient than pointanimation. In WPF, you can implement a pointcollectionanimamtion by inheriting timeline, which you can refer to in this project. Unfortunately, although the timeline class of the UWP is not closed, it is completely unknown how to inherit and derive a custom animation.
It's time to think a little bit. DoubleAnimation can be understood as this: Storyboard passes a timespan to DoubleAnimation, DoubleAnimation the current value of the target attribute is finally passed to the target property through this timespan (sometimes combined with easingfunction), as shown in:
In this case, you can also receive the computed double, and then calculate the pointcollection value of the target by converter:
Suppose to tell this converter when the incoming double value (named progress) is 0, the pointcollection is {0,0 ...},progress is 100 when pointcollection is {2,2 ...}, When the progress is in any of these values, the calculation method is:
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;}
This completes the conversion process from TimeSpan to pointcollection. Then you define how you use it on the XAML. Refer to the above pointcollectionanimation, although more than a converter, but the XAML should be concise enough:
<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= "10 0 "fillbehavior=" HoldEnd "storyboard.targetproperty=" (LOCAL:PROGRESSTOPOINTC ollectionbridge.progress) "Storyboard.targetname=" Progresstopointcollectionbridge " enabledependentanimation= "True"/></storyboard>...<polygon x:name= "Polygon" points= "{Binding so Urce={staticresource progresstopointcollectionbridge},path=points} " Stroke= "Darkolivegreen" strokethickness= "2" height= "+" width= "250" Stretch= "Fill"/>
In the end I chose to name this converter ProgressToPointCollectionBridge
. You can see that polygon binds points to progresstopointcollectionbridge,doubleanimation Change the progresstopointcollectionbridge.progress to change the points. The simplicity of XAML is satisfactory, and if you need to manipulate multiple points, the advantages over pointanimation are great.
The results of the operation are as follows:
Full 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= "fillbehavior=" HoldEnd "Storyboard.ta Rgetproperty= "(local:ProgressToPointCollectionBridge.Progress)" Storyboard.targetname= "Progress Topointcollectionbridge "enabledependentanimation=" True "><doubleanimation.easingfunction& Gt;<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 "Strok ethickness= "2" height= "width=" "stretch=" Fill "fill=" # FFEBF412 "/></grid>
Progresstopointcollectionbridge:
[Contentproperty (Name = nameof (children))]public class Progresstopointcollectionbridge:dependencyobject{public Progresstopointcollectionbridge () {children = new observablecollection<pointcollection> (); }///<summary>///Gets or sets the value of the Points//</summary>public pointcollection Points {get {return (pointcollecti ON) GetValue (Pointsproperty); }set {SetValue (pointsproperty, value);} }///<summary>///Gets or sets the value of the Progress//</summary>public double Progress {get {return (double) GetValue (P Rogressproperty); }set {SetValue (progressproperty, value);} }///<summary>///Gets or sets the value of 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 Inotifyc Ollectionchanged;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 (v Ar 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. Conclusion
If DoubleAnimation is said to animate the double property of the target, the pointanimation can be said to animate Point.x and Point.y two double properties simultaneously, and ColorAnimation is "Animate the Color.a, R, G, b four int properties of a target at the same time." This understanding of the words pointanimation and coloranimation is only an extension of the doubleanimation, further said, through the DoubleAnimation should be able to extend the animation of all types of properties. But I don't know how to customize animations on the UWP, but only through the compromise of this article. Although the XAML needs to be more complex to write, it also has its benefits:
There is no need to know too much about animation related classes, only the basic knowledge of dependency properties, bindings, etc. is sufficient.
will not change due to changes in the animation API, it is compatible with WPF, Silverlight, and UWP (I guess I didn't really test the code on WPF).
The code is simple enough to eliminate the steps to calculate the timespan and Easingfunction. A slightly modified type can also be made generic AnimationBridge < T >
, providing support for data types other than pointcollection.
In combination with the previous article and then Divergent, always feel that the future encounter what the UWP does not provide functionality can be achieved through a flexible approach, binding and DependencyProperty is really the best friend of UWP developers.
4. Reference
How SVG Shape morphing Works
Gadal Metasyllabus