目录
基础知识
用来制作动画的属性必须是依赖属性。动画实际是一段时间内各种属性值的连续变化。
简单动画称为AnimationTimeline,复杂动画需要多个UI元素合作完成,称为Storyboard。
几乎针对每个可用的数据类型,都准备了相应的动画派生类。
不是所有抽象基类都有这三个的,有的会缺。
简单线性动画
四要素:
- 变化起点:From,如果没有指定,则以变化目标属性的当前值为起点
- 变化终点:To,如果没有指定,则以上次动画的终点或默认值为变化终点
- 变化幅度:By,如果同时指定了变化终点,变化幅度将被忽略
- 变化时间:Duration,必须指定,数据类型也是Duration
实例:按钮移动
每次点击按钮,按钮会从(0,0)也就是窗体左上角向右下角移动,x,y都不超过300,完成运动的时长是300ms。
如果不指定的话,会在上一个终点的基础上跳。
前台
<Grid>
<Button Content="Move!" HorizontalAlignment="Left" VerticalAlignment="Top"
Width="60" Height="60" Click="Button_Click">
<Button.RenderTransform>
<TranslateTransform x:Name="tt" X="0" Y="0"/>
</Button.RenderTransform>
</Button>
</Grid>
后台
private void Button_Click(object sender, RoutedEventArgs e)
{
DoubleAnimation daX = new DoubleAnimation();
DoubleAnimation daY = new DoubleAnimation();
//指定起点
daX.From = 0D;
daY.From = 0D;
//指定终点
Random r = new Random();
// NextDouble是产生0~1的double
daX.To = r.NextDouble() * 300;
daY.To = r.NextDouble() * 300;
Console.WriteLine(daX.To);
//指定时长
Duration duration = new Duration(TimeSpan.FromMilliseconds(300));//返回指定的毫秒数
daX.Duration = duration;
daY.Duration = duration;
// 动画的主体是TranslateTransform,非button
this.tt.BeginAnimation(TranslateTransform.XProperty, daX); // XProperty和YProperty都是依赖属性
this.tt.BeginAnimation(TranslateTransform.YProperty, daY);
}
如果不指定起点,不指定终点,只令
daX.By = 100D;
daY.By = 100D;
那么按钮每次会往右下角移动。
如果希望调整Button的宽度或高度(也是Double类型),那也是上面类似的操作,只是要影响的是Button.WidthProperty和Button.HeightProperty (测试没通过,以后我再补充吧)
高级动画
- AccelerationRatio,加速速率,[0, 1],与DecelerationRatio之和不大于1.0, 模拟汽车启动
- DecelerationRatio,减速速率,[0, 1],模拟汽车刹车
- SpeedRatio,动画实际播放速度与正常速度的比值,快进播放、慢动作
- AutoReverse,是否以相反的动画方式从终止值返回起始值,倒退播放
- RepearBehavior,动画的重复行为,0表示不播放,double值控制循环次数,RepeatBehavior.Forever表示永远循环,循环播放
- BeginTime,正式开始播放前的等待时间,多个动画之前的协同
- EasingFunction,缓冲式渐变,乒乓球弹跳效果(取值是IEasingFunction接口,派生类很多)
实例:按钮弹跳
private void Button_Click(object sender, RoutedEventArgs e)
{
DoubleAnimation daX = new DoubleAnimation();
DoubleAnimation daY = new DoubleAnimation();
//指定起点
daX.From = 0D;
daY.From = 0D;
BounceEase be = new BounceEase();
be.Bounces = 3;//弹跳3次
be.Bounciness = 3;//弹性程度,值越大反弹越低
daY.EasingFunction = be;
//指定终点
daX.To = 300;
daY.To = 300;
//指定时长
Duration duration = new Duration(TimeSpan.FromMilliseconds(2000));//返回指定的毫秒数
daX.Duration = duration;
daY.Duration = duration;
// 动画的主体是TranslateTransform,非button
this.tt.BeginAnimation(TranslateTransform.XProperty, daX);
this.tt.BeginAnimation(TranslateTransform.YProperty, daY);
}
关键帧动画
动画是UI元素属性连续改变所产生的视觉效果。属性的每次变化都会产生一个新的画面,这个新画面称为一“帧”。单位时间内播放的帧数越多,动画的效果越细致。前面的简单动画只设置了起点和终点,之间的动画帧是由程序计算并绘制出来的,程序猿无法控制。关键帧动画允许程序猿为一段动画设置几个里程碑,当动画执行到里程碑所在的时间点时,被动画控制的属性也必须达到设定的值。这种里程碑就是关键帧。
实例:按钮Z形移动
要求button在被单击后用900ms从左上角移动至右下角,走Z字形(可以几段简单动画拼一起,但是一旦需求改动,不好改)。
<Grid>
<Button Content="Move" VerticalAlignment="Top" HorizontalAlignment="Left"
Width="80" Height="80" Click="Button_Click">
<Button.RenderTransform>
<TranslateTransform x:Name="tt" X="0" Y="0"/>
</Button.RenderTransform>
</Button>
</Grid>
private void Button_Click(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));
//为了x
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)); //FromTimeSpan是绝对时间
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);
//为了y
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)); //FromTimeSpan是绝对时间
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()是以百分比计算相对时间点的,全长是100%。这样避免了延长时间时需要再改绝对值。可以只改Duration。
如果使用KeyTime.FromPercent(0.33), 0.66, 1.0,也可以有同样的效果。
特殊关键帧
DoubleAnimationUsingKeyFrames的属性KeyFrames的数据类型是DoubleKeyFrameCollection,可以接收元素类型为DoubleKeyFrame的对象。DoubleKeyFrame是一个抽象类,派生类有:
- LinearDoubleKeyFrame:目标属性值是均匀变化的
- DiscreteDoubleKeyFrame:目标属性值的变化是跳跃性的、跃迁的
- SplineDoubleKeyFrame:目标属性值的变化速率是一条贝塞尔曲线(常用)
- EasingDoubleKeyFrame:目标属性值以某种缓冲形式变化
SplineDoubleKeyFrame实例
它的贝塞尔曲线有两个控制点ControlPoint1和ControlPoint2,如果这两点的自己的横纵坐标值相等,如(0.5,0.5)这样,那么贝塞尔曲线是直线,则与LinearDoubleKeyFrame等价。
private void Button_Click(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);
}
说明:移动起来还是挺有动感的,可以试试。常用SplineDoubleKeyFrame代替LinearDoubleKeyFrame。
路径动画
让目标对象沿一条给定的路径移动。使用DoubleAnimationUsingPath类。需要一个PathGeometry指明路径。Source取值PathAnimationSource.X的话,表示动画关注每一点横坐标的变化,如果是PathAnimationSource.Y则关注每一点纵坐标的变化,如果是.Angle则关注每一点处切线方向的变化。
实例:button沿一条贝塞尔曲线做波浪线形运动
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<!--移动路径-->
<PathGeometry x:Key="movingPath" Figures="M 0,150 C300,-100 300,400 600,120"/>
</Grid.Resources>
<Button Content="Move" HorizontalAlignment="Left" VerticalAlignment="Top"
Width="80" Height="80" Click="Button_Click">
<Button.RenderTransform>
<TranslateTransform x:Name="tt" X="0" Y="0"/>
</Button.RenderTransform>
</Button>
</Grid>
private void Button_Click(object sender, RoutedEventArgs e)
{
PathGeometry pg = this.LayoutRoot.FindResource("movingPath") as PathGeometry;
Duration duration = new Duration(TimeSpan.FromMilliseconds(600));
//创建动画
DoubleAnimationUsingPath dapX = new DoubleAnimationUsingPath();
dapX.PathGeometry = pg;
dapX.Source = PathAnimationSource.X;
dapX.Duration = duration;
// y
DoubleAnimationUsingPath dapY = new DoubleAnimationUsingPath();
dapY.PathGeometry = pg;
dapY.Source = PathAnimationSource.Y;
dapY.Duration = duration;
//执行
this.tt.BeginAnimation(TranslateTransform.XProperty, dapX);
this.tt.BeginAnimation(TranslateTransform.YProperty, dapY);
}
如果加上下面几句就能来回来去地跑了:
dapX.AutoReverse = true;
dapX.RepeatBehavior = RepeatBehavior.Forever;
dapY.AutoReverse = true;
dapY.RepeatBehavior = RepeatBehavior.Forever;
场景
场景就是并行执行一组动画。选好触发时机。一般用前端设计,后台设计很麻烦。
实例:三只小球同时运动
点击按钮,三个小球会移动过去。
<Grid Margin="6">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="Gray" BorderThickness="1" Grid.Row="0">
<Ellipse x:Name="ballR" Height="48" Width="48" Fill="Red"
HorizontalAlignment="Left">
<Ellipse.RenderTransform>
<TranslateTransform x:Name="ttR"/>
</Ellipse.RenderTransform>
</Ellipse>
</Border>
<Border BorderBrush="Gray" BorderThickness="1,0,1,1" Grid.Row="1">
<Ellipse x:Name="ballG" Height="48" Width="48" Fill="LawnGreen"
HorizontalAlignment="Left">
<Ellipse.RenderTransform>
<TranslateTransform x:Name="ttG"/>
</Ellipse.RenderTransform>
</Ellipse>
</Border>
<Border BorderBrush="Gray" BorderThickness="1,0,1,1" Grid.Row="2">
<Ellipse x:Name="ballB" Height="48" Width="48" Fill="Blue"
HorizontalAlignment="Left">
<Ellipse.RenderTransform>
<TranslateTransform x:Name="ttB"/>
</Ellipse.RenderTransform>
</Ellipse>
</Border>
<Button Content="Button" Grid.Column="1" Grid.RowSpan="3">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard Duration="0:0:0.6">
<!--红色小球动画-->
<DoubleAnimation Duration="0:0:0.6" To="400"
Storyboard.TargetName="ttR"
Storyboard.TargetProperty="X"/>
<!--绿色小球动画-->
<DoubleAnimationUsingKeyFrames Duration="0:0:0.6"
Storyboard.TargetName="ttG"
Storyboard.TargetProperty="X">
<SplineDoubleKeyFrame KeyTime="0:0:0.6" Value="400"
KeySpline="1,0,0,1"/>
</DoubleAnimationUsingKeyFrames>
<!---->
<DoubleAnimationUsingKeyFrames Duration="0:0:0.6"
Storyboard.TargetName="ttB"
Storyboard.TargetProperty="X">
<SplineDoubleKeyFrame KeyTime="0:0:0.6" Value="400"
KeySpline="0,1,1,0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>