WPF规定,可以用来制作动画的属性必须是依赖属性。
Timeline、AnimationTimeline和Storyboard
⒈简单线性动画
所谓“简单线性动画”就是指仅由变化起点、变化终点、变化幅度、变化时间4个要素构成的动画。
变化时间(Duration属性):必须指定,数据类型为Duration。
变化终点(To属性):如果没有指定变化终点,程序将采用上一次动画的终点或默认值。
变化幅度(By属性):如果同时指定了变化终点,变化幅度将被忽略。
变化起点(From属性):如果没有指定变化起点则以变化目标属性的当前值为起点。
private void Button_OnClick(object sender, RoutedEventArgs e) { DoubleAnimation daX=new DoubleAnimation(); DoubleAnimation daY=new DoubleAnimation(); //指定起点 daX.From = 0D; daY.From = 0D; //指定终点 Random r=new Random(); daX.To= r.NextDouble() * 300; daY.To= r.NextDouble() * 300; //指定时长 Duration duration=new Duration(TimeSpan.FromMilliseconds(300)); daX.Duration = duration; daY.Duration = duration; //动画的主体是TranslateTransform变形,而非Button this.tt.BeginAnimation(TranslateTransform.XProperty, daX); this.tt.BeginAnimation(TranslateTransform.YProperty, daY); }
这段代码有以下几处值得注意的地方:
⒈因为指定了daX和daY的起始值为0,所以每次按钮都会“跳”回窗体的左上角开始动画。如果想让按钮从当前位置开始下一次动画,只需要把“daX.From=0D;”和“daY.From=0D;”两句代码移除即可。
⒉尽管表现出来的是Button在移动,但DoubleAnimation的作用目标并不是Button而是TranslateTransform实例,因为TranslateTransform实例是Button的RenderTransform属性值,所以Button“看上去”是移动。
⒊能用来制作动画的属性必须是依赖属性,TranslateTransform的XProperty和YProperty就是两个依赖属性。
⒋UIElement和Animatable两个类都定义有BeginAnimation这个方法,TranslateTransform派生自Animatable类,所以具有这个方法,这个方法的调用者就是动画要作用的目标对象,两个参数分别指明被用作依赖属性(TranslateTransform.XProperty和TranslateTransform.XProperty)和设计好的动画(daX和daY),可以猜想,如果需要动画改编Button的宽度或高度(这两个属性也是Double类型),也应该先创建DoubleAnimation实例,然后设置起始值和动画时间,最后用Button的BeginAnimation方法、使用动画对象影响Buttion.WidthProperty和Button.HeightProperty。
⒉高级动画控制
使用From、To、By、Duration几个属性进行组合就已经可以制作很多不同效果的动画了,然而WPF动画系统提供的控制苏醒远不止这些。如果想制作更加复杂或逼真的动画,还需要使用以下一些效果。
属性 | 描述 | 应用举例 |
AccelerationRatio | 加速速率,介于0.0和1.0之间,与DecelerationRatio之和不大于1.0 | 模拟汽车启动 |
DecelerationRatio | 减速速率,介于0.0和1.0之间,与AccelerationRatio之和不大于1.0 | 模拟汽车刹车 |
SpeedRatio | 动画实际播放速度与正常速度的比值 | 快进播放、慢动作 |
AutoReverse | 是否以相反的动画方式从终止方起始值 | 倒退播放 |
RepeatBehavior | 动画的重复行为,取0为不播放,使用double类型值可控制循环次数,取RepeatBehavior.Forever为永远循环 | 循环播放 |
BeginTime | 正式开始播放前的等待时间 | 多个动画之前的协同 |
EasingFunction | 缓冲式渐变 | 乒乓球弹跳效果 |
对于这些属性,大家可以自己动手尝试----对它们进行组合往往可以产生很多意想不到的效果。
在这些属性中,EasingFunction是一个扩展性非常强的属性。它的取值是IEasingFunction接口类型,为WPF自带的IEasingFunction派生类就有十多种,每个派生类都能产生不停的结束效果。比如BounceEase可以产生乒乓球弹跳式的效果,我们可以直接拿来使用而不必花精力去亲自创作。
private void Button_OnClick(object sender, RoutedEventArgs e) { DoubleAnimation daX = new DoubleAnimation(); DoubleAnimation daY = new DoubleAnimation(); //设置反弹 BounceEase be=new BounceEase(); be.Bounces = 3; //弹跳3次 be.Bounciness = 3; //弹性程度,值越大反弹越低 daY.EasingFunction = be; //指定起点 daX.From = 0D; daY.From = 0D; //指定终点 daX.To = 300; daY.To = 300; //指定时长 Duration duration = new Duration(TimeSpan.FromMilliseconds(3000)); daX.Duration = duration; daY.Duration = duration; //动画的主体是TranslateTransform变形,而非Button this.tt.BeginAnimation(TranslateTransform.XProperty, daX); this.tt.BeginAnimation(TranslateTransform.YProperty, daY); }
⒊关键帧动画
动画是UI元素属性连续改变所产生的视觉效果。属性每次细微的变化都会产生一个新的画面,每个新画面就称为一“帧”,帧的连续播放就产生动画效果。如同电影一样,单位时间内播放的帧数越多,动画的效果就越细致。前面讲到的简单动画只设置了一个起点和终点,之间的动画帧都是由程序计算出来并绘制的,程序员无法进行控制。关键帧动画则允许程序员为一段动画设置几个“里程碑”,动画执行到里程碑所在的时间点时,被动画所控制的属性值也必须达到设定的值,这些时间线上的“里程碑”就是关键帧。
private void Button_OnClick(object sender, RoutedEventArgs e) { DoubleAnimationUsingKeyFrames dakX=new DoubleAnimationUsingKeyFrames(); DoubleAnimationUsingKeyFrames dakY=new DoubleAnimationUsingKeyFrames(); //设置动画总时长 dakX.Duration=new Duration(TimeSpan.FromMilliseconds(900)); dakY.Duration=new Duration(TimeSpan.FromMilliseconds(900)); //创建、添加关键帧 LinearDoubleKeyFrame x_kf_1 = new LinearDoubleKeyFrame(); LinearDoubleKeyFrame x_kf_2 = new LinearDoubleKeyFrame(); LinearDoubleKeyFrame x_kf_3 = new LinearDoubleKeyFrame(); x_kf_1.KeyTime=KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300)); x_kf_1.Value = 200; x_kf_2.KeyTime=KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(600)); x_kf_2.Value = 0; x_kf_3.KeyTime=KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(900)); x_kf_3.Value = 200; dakX.KeyFrames.Add(x_kf_1); dakX.KeyFrames.Add(x_kf_2); dakX.KeyFrames.Add(x_kf_3); LinearDoubleKeyFrame y_kf_1 = new LinearDoubleKeyFrame(); LinearDoubleKeyFrame y_kf_2 = new LinearDoubleKeyFrame(); LinearDoubleKeyFrame y_kf_3 = new LinearDoubleKeyFrame(); y_kf_1.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300)); y_kf_1.Value = 0; y_kf_2.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(600)); y_kf_2.Value = 180; y_kf_3.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(900)); y_kf_3.Value = 180; dakY.KeyFrames.Add(y_kf_1); dakY.KeyFrames.Add(y_kf_2); dakY.KeyFrames.Add(y_kf_3); //执行动画 this.tt.BeginAnimation(TranslateTransform.XProperty, dakX); this.tt.BeginAnimation(TranslateTransform.YProperty, dakY); }
使用 KeyTime.FromPercent静态方法则可以获得以百分比计算单的相对时间点,程序将整个关键帧动画的时长(Duration)视为100%。
x_kf_1.KeyTime = KeyTime.FromPercent(0.33); x_kf_1.Value = 200; x_kf_2.KeyTime = KeyTime.FromPercent(0.66); x_kf_2.Value = 0; x_kf_3.KeyTime = KeyTime.FromPercent(1.0); x_kf_3.Value = 200;
之后无论你把dakX的Duration改为多少,三个关键帧都会将整个动画分割为均等的三段。
⒋特殊的关键帧
DoubleAnimationUsingKeyFrames的KeyFrames属性的数据类型是DoubleKeyFrameCollection,此集合类可接收的元素类型为DoubleKeyFrame。DoubleKeyFrame是一个抽象类,前面使用的LinearDoubleKeyFrame就是它的派生类之一。DoubleKeyFrame的所有派生类如下:
LinearDoubleKeyFrame:线性变换关键帧,目标属性值的变化是直线性的、均匀的,及变化速率不变。
DiscreteDoubleKeyFrame:不连续变化关键帧,目标属性值的变化是跳跃性的、跃迁的。
SplineDoubleKeyFrame:样条函数式变化关键帧,目标属性值得变化速率是一条贝塞尔曲线。
EasingDoubleKeyFrame:缓冲式变化关键帧,目标属性值以某种缓冲形式变化。
4个派生类中最常用的是SplineDoubleKeyFrame(SplineDoubleKeyFrame可以替代LinearDoubleKeyFrame)。
private void Button_OnClick(object sender, RoutedEventArgs e) { //创建动画 DoubleAnimationUsingKeyFrames dakX=new DoubleAnimationUsingKeyFrames(); dakX.Duration=new Duration(TimeSpan.FromMilliseconds(1000)); //创建、添加关键帧 SplineDoubleKeyFrame kf=new SplineDoubleKeyFrame(); kf.KeyTime=KeyTime.FromPercent(1); kf.Value = 400; KeySpline ks=new KeySpline(); ks.ControlPoint1 = new Point(0, 1); ks.ControlPoint2 = new Point(1, 0); kf.KeySpline = ks; dakX.KeyFrames.Add(kf); //执行动画 this.tt.BeginAnimation(TranslateTransform.XProperty,dakX); }
⒌路径动画
如何让目标对象沿着一条给定的路径移动呢?答案是使用DoubleAnimationUsingPath类。DoubleAnimationUsingPath需要一个PathGeometry来指明移动路径,PathGeometry的数据信息可以用XAML的Path语法书写。PathGeometry的另一个重要属性是Source,Source属性的数据类型是PathAnimationSource枚举,枚举值可取X、Y或Angle。如果路径动画Source属性的取值是PathAnimationSource.X,意味着这个动画关注的是曲线上每一点横坐标的变化;如果路径动画Source属性的取值是PathAnimationSource.Y,意味着这个动画关注的是曲线上每一点纵坐标的变化;如果路径动画Source属性的取值是PathAnimationSource.Angle,意味着这个动画关注的是曲线上每一点处切线方向的变化。
private void Button_OnClick(object sender, RoutedEventArgs e) { // 从XAML代码中获取移动路径数据 System.Windows.Media.PathGeometry pg=this.LayoutRoot.FindResource("movingPath") as System.Windows.Media.PathGeometry; Duration duration=new Duration(TimeSpan.FromMilliseconds(600)); //创建动画 DoubleAnimationUsingPath dapX=new DoubleAnimationUsingPath(); dapX.PathGeometry = pg; dapX.Source = PathAnimationSource.X; dapX.Duration = duration; DoubleAnimationUsingPath dapY=new DoubleAnimationUsingPath(); dapY.PathGeometry = pg; dapY.Source=PathAnimationSource.Y; dapY.Duration = duration; // 自动返回、永远循环 dapX.AutoReverse = true; dapX.RepeatBehavior=RepeatBehavior.Forever; ; dapY.AutoReverse = true; dapY.RepeatBehavior=RepeatBehavior.Forever; ; // 执行动画 this.tt.BeginAnimation(TranslateTransform.XProperty, dapX); this.tt.BeginAnimation(TranslateTransform.YProperty, dapY); }