WPF动画分类
在WPF中,与动画相关的的类型具体如下:
17个类型名 + Animation
。22个类型名 + AnimationUsingKeyFrames
。3个类型名 + AnimationUsingPath
。尽管看起来很多,但实际上里面由很多是在使用上都是类似的,而且在实际开发过程中正真用到的只是其中的小部分,其中用的最多的是简单线性动画,所以不必慌张。
WPF动画中的关键对象
WPF中实现动画由两个关键的因素,即动画类与故事板。
动画类需要根据不同的需求、场景来进行选择。
故事板在动画开发中也是十分重要的,故事板相当于动画的舞台,而动画类则相当于动画的演员。舞台上根据时间线在指定的时间节点安排演员上场,组成完整动画。
WPF动画的必要条件
对象必须实现Animatable
接口(界面控件基本都实现了)。
关联属性必须是依赖属性。
需要有与属性对应类型的动画类,比如说控件的Width
属性,其类型是Double
类型,Double
类型对应的动画类型为DoubleAnimation
。而有些属性是没有对应的动画类的,比如string
类型是没有对应的简单线性动画类的,此时如果是关键帧动画,而恰好要进行动画的属性是没有动画类型的,可以使用ObjectAnimationUsingKeyFrames
。
StoryBoard
对象用于控制动画的运行,包括开始、停止、暂停、恢复等。
此外,当我们定义了动画实例后,这个动画实例要执行在哪个对象上,也是需要通过StoryBoard
的附加属性来处理的。
StoryBoard
元素中可以放入多个动画对象。
故事板中的常用属性
SpeedRatio
:动画过程的速率,不影响动画时间,只是按照比例加快原有速度。
AccelerationRatio
、DecelerationRatio
:设置动画在过程时间中的加速和减速,有效值为0-1,表示在动画过程时间的百分比时间内进行加速或减速。
AutoReverse
:动画完成后是否执行相反的动画。
FillBehavior
:动画结束结束后控件的状态。
HoldEnd
:保持动画结束后的状态。(默认)Stop
:回到动画开始前的状态。RepeatBehavior
:动画重复方式,包括三种值:Forever
、次数(3x)、时间(0:0:10)
关于故事板的其他操作,放在本章最后。
简单线性动画相关的常用类型共有17个之多,用起来基本上都差不多,因此这里挑几个比较常用的作为例子进行学习。
Duration
:时间,时:分:秒,例:Duration="0:0:1"
From
:起始数据,可以不设置,直接从当前控件对象中获取数据。
To
:结束数据。
By
:从控件对象对应属性的当前值开始增量变化到指定数值,比如原先是50,那么By
设定成200的最终值就是250。设置了By
属性则不需要在设置From
和To
。
Storyboard.TargetName
:故事板的附加依赖属性,用于指定动画的作用对象。
Storyboard.TargetProperty
:故事板的附加依赖属性,用于指定动画的作用属性。
RepeatBehavior
:动画重复,可以设置重复的次数、时间或者永远重复。
Forever
:永远重复。StoryBorad
中也有RepeatBehavior
属性,其对所有的子动画元素生效,优先级低于子动画元素在内联方式的重复设置。BeginTime
:设置动画的延时开始的时间,时:分:秒。
AutoReverse
:动画完成后是否自动折返(恢复)。
控件宽度变化
Xaml中进行动画设定
<Window ......>
<Window.Resources>
<Storyboard x:Key="sb">
<DoubleAnimation
Duration="0:0:1"
From="50"
To="300"
Storyboard.TargetName="bd"
Storyboard.TargetProperty="Width"/>
</Storyboard>
</Window.Resources>
<DockPanel>
<Button Content="动画" Click="Button_Click" DockPanel.Dock="Bottom" Name="btn_Begin"/>
<Border x:Name="bd" Background="Yellow" Height="50" Width="50"/>
</DockPanel>
</Window>
Click
事件函数中开启动画
private void Button_Click(object sender, RoutedEventArgs e)
{
(Resources["sb"] as Storyboard).Begin();
}
除了在代码中开启动画之外,还可以在XAML中通过事件触发器来开启动画
<Window.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_Begin">
<BeginStoryboard Storyboard="{StaticResource sb}"/>
</EventTrigger>
</Window.Triggers>
控件的移动
控件的移动可以通过控制Margin
属性来实现
<Window.Resources>
<Storyboard x:Key="sb">
<ThicknessAnimation
Duration="0:0:1"
To="100 0 0 0"
Storyboard.TargetName="bd"
Storyboard.TargetProperty="Margin"
AutoReverse="True"
RepeatBehavior="Forever"/>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_Begin">
<BeginStoryboard Storyboard="{StaticResource sb}"/>
</EventTrigger>
</Window.Triggers>
<DockPanel>
<Button Content="动画" DockPanel.Dock="Bottom" Name="btn_Begin"/>
<Border x:Name="bd" Background="Yellow" Height="50" Width="50"/>
</DockPanel>
关于动画类型的选用,根据要进行动画的属性去选用就可以了,比如Width
的类型为Double
,那么就用DoubleAnimation
动画类型;Margin
的类型为Thinkness
,则使用ThicknessAnimation
动画类型。
DoubleAnimation
和ThicknessAnimation
动画类型的使用是比较直接的,需要注意的是ColorAnimation
类型由于此动画类型多用于Background
、Fill
等属性上,而这些属性并不是单纯的插值属性,而是Brush
画刷对象,ColorAnimation
所对应的应该是Brush
画刷对象中的Color
属性,因此正确的用法如下:
......
<ColorAnimation Duration="0:0:0" To="Red" Storyboard.TargetProperty="Color" Storyboard.TargetName="LineStroke"/>
......
<Line ......>
<Line.Stroke>
<SolidColorBrush x:Name="LineStroke" Color="Blue"/>
</Line.Stroke>
</Line>
也可以用如下简化写法
......
<ColorAnimation Duration="0:0:0" To="Red" Storyboard.TargetProperty="(Line.Stroke).(SolidColorBrush.Color)" Storyboard.TargetName="line"/>
......
<Line ...... x:Name="line" Stroke="Blue"/>
关键帧动画的关键帧类型是根据不同的动画特性来进行分类的,主要分为线性变化关键帧、离散变化关键帧、样条关键帧、缓冲式关键帧。
此外,在xaml中,关键帧元素是要放到关键帧动画类型元素中的,而关键帧动画类型是根据要进行动画的元素的属性类型来区分的,比如要进行动画变化的是Text
属性,Text
属性是string
类型,所以使用StringAnimationUsingKeyFrames
关键帧动画类型。
Linear+[类型]+KeyFrame
:线性变化关键帧,跟简单线性动画的处理基本一样,差别在于可以在时间线中自由设置每个关键帧之间的时间及变化。
KeyTime
:从初始值到目标值的过程时间,时:分:秒。Value
:属性的目标值。示例
<Window.Resources>
<Storyboard x:Key="sb">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="border" Storyboard.TargetProperty="Width">
<LinearDoubleKeyFrame KeyTime="0:0:2" Value="150"/>
<LinearDoubleKeyFrame KeyTime="0:0:4" Value="100"/>
<LinearDoubleKeyFrame KeyTime="0:0:6" Value="150"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource sb}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<StackPanel>
<Border Background="Yellow" Width="60" Height="30" Name="border"/>
<Button Content="开始动画" Name="btn_Start"/>
</StackPanel>
</Grid>
Discrete+[类型]+KeyFrame
:离散变化关键帧,到时间节点立即变化,过程中不发生连续变化。
KeyTime
:关键帧的插入时间,时:分:秒。Value
:插入关键帧后,属性的目标值。示例
<Window.Resources>
<Storyboard x:Key="sb">
<StringAnimationUsingKeyFrames Storyboard.TargetName="txtb_Info" Storyboard.TargetProperty="Text">
<DiscreteStringKeyFrame KeyTime="0:0:0" Value="H"/>
<DiscreteStringKeyFrame KeyTime="0:0:0.5" Value="He"/>
<DiscreteStringKeyFrame KeyTime="0:0:1" Value="Hel"/>
<DiscreteStringKeyFrame KeyTime="0:0:1.5" Value="Hell"/>
<DiscreteStringKeyFrame KeyTime="0:0:2" Value="Hello"/>
</StringAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource sb}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<StackPanel>
<TextBlock Name="txtb_Info"/>
<Button Content="开始动画" Name="btn_Start"/>
</StackPanel>
</Grid>
Spline+[类型]+KeyFrame
:样条关键帧,可以使用样条函数(二次贝塞尔曲线-Path),来调整动画的过程。
KeyTime
:从初始值到目标值的过程时间,时:分:秒。Value
:属性的目标值。KeySpline
:设置二次贝塞尔曲线的控制点,分别为第一个控制点和第二个控制点,KeySpline="0.2,0.3 0.8,0.4"
。这里的贝塞尔曲线上的坐标系是以过程时间作为x轴分为0-1,起始值到目标值作为y轴分为0-1形成的。(可以打开Blend查看)示例
<Window.Resources>
<Storyboard x:Key="sb">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="tt" Storyboard.TargetProperty="X" AutoReverse="True">
<SplineDoubleKeyFrame KeyTime="0:0:3" Value="375" KeySpline="0.2,0.3 0.3,0.8"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource sb}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<StackPanel>
<Border Background="Yellow" Width="400" Height="30" Name="border">
<Ellipse Width="25" Height="25" Stroke="White" Fill="White" HorizontalAlignment="Left">
<Ellipse.RenderTransform>
<TranslateTransform X="0" x:Name="tt"/>
</Ellipse.RenderTransform>
</Ellipse>
</Border>
<Button Content="开始动画" Name="btn_Start"/>
</StackPanel>
</Grid>
EasingDoubleKeyFrame
:缓动关键帧,使用WPF中预置的动画效果。
KeyTime
:从初始值到目标值的过程时间,时:分:秒。Value
:属性的目标值。EasingFunction
:应用于该关键帧的缓动函数,EasingFunction
并不只是对EasingDoubleKeyFrame
有效,对简单线性动画也是支持的。
BounceEase
:乒乓球的弹跳效果。示例
<Window.Resources>
<Storyboard x:Key="sb">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="border" Storyboard.TargetProperty="Width">
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="200">
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource sb}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<StackPanel>
<Border Background="Yellow" Width="50" Height="30" Name="border"/>
<Button Content="开始动画" Name="btn_Start"/>
</StackPanel>
</Grid>
路径动画可以根据Path微语言
数据来设定动画行走轨迹。
路径动画类型只有三种,分别为DoubleAnumationUsingPath
、PoinAnimationUsingPath
和MatrixAnimationUsingPath
。
DoubleAnumationUsingPath
:用于进行单个数值的路径动画实现。
Storyboard.TargetName
:Storyboard
的附加依赖属性,设置实现动画的目标控件。Storyboard.TargetProperty
:Storyboard
的附加依赖属性,设置实现动画的具体属性。Duration
:从初始值到目标值的过程时间。Source
:从路径微语言中获取哪个属性。PathGeometry
:路径几何对象,通过Figures
属性使用微语言来设置动画路径。示例
<Window.Resources>
<Storyboard x:Key="pathAnimation">
<DoubleAnimationUsingPath Storyboard.TargetName="ttMove"
Storyboard.TargetProperty="X"
Duration="0:0:4"
Source="X">
<DoubleAnimationUsingPath.PathGeometry>
<PathGeometry Figures="M0,0 L100,100 a100,100,0,0,0,200,200"/>
</DoubleAnimationUsingPath.PathGeometry>
</DoubleAnimationUsingPath>
<DoubleAnimationUsingPath Storyboard.TargetName="ttMove"
Storyboard.TargetProperty="Y"
Duration="0:0:4"
Source="Y">
<DoubleAnimationUsingPath.PathGeometry>
<PathGeometry Figures="M0,0 L100,100 a100,100,0,0,0,200,200"/>
</DoubleAnimationUsingPath.PathGeometry>
</DoubleAnimationUsingPath>
<DoubleAnimationUsingPath Storyboard.TargetName="rtMove"
Storyboard.TargetProperty="Angle"
Duration="0:0:4"
Source="Angle">
<DoubleAnimationUsingPath.PathGeometry>
<PathGeometry Figures="M0,0 L100,100 a100,100,0,0,0,200,200"/>
</DoubleAnimationUsingPath.PathGeometry>
</DoubleAnimationUsingPath>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_Move">
<BeginStoryboard Storyboard="{StaticResource pathAnimation}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<Rectangle Name="e_Move" Fill="Red" Width="30" Height="30" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="0.5 0.5">
<Rectangle.RenderTransform>
<TransformGroup>
<!--这里注意变形的顺序-->
<RotateTransform Angle="0" x:Name="rtMove"/>
<TranslateTransform X="0" Y="0" x:Name="ttMove"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Button Name="btn_Move" Width="60" Height="20" Content="开始动画"/>
</Grid>
PointAnimationUsingPath
:针对控件的Point
类型属性进行动画实现。
Storyboard.TargetName
:Storyboard
的附加依赖属性,设置实现动画的目标控件。Storyboard.TargetProperty
:Storyboard
的附加依赖属性,设置实现动画的具体属性。Duration
:从初始值到目标值的过程时间。PathGeometry
:路径几何对象,通过Figures
属性使用微语言来设置动画路径。PointAnimationUsingPath
并不需要设置路径的数值来源,会自动获取路径的X
、Y
来作为Point
的值。
示例
<Window.Resources>
<Storyboard x:Key="pathAnimation">
<PointAnimationUsingPath Storyboard.TargetName="ellipse"
Storyboard.TargetProperty="Center"
Duration="0:0:4">
<PointAnimationUsingPath.PathGeometry>
<PathGeometry Figures="M20,20 L100,100 a100,100,0,0,0,200,200"/>
</PointAnimationUsingPath.PathGeometry>
</PointAnimationUsingPath>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_Move">
<BeginStoryboard Storyboard="{StaticResource pathAnimation}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<Path Fill="Red">
<Path.Data>
<EllipseGeometry Center="20 20" RadiusX="20" RadiusY="20" x:Name="ellipse"/>
</Path.Data>
</Path>
<Button Name="btn_Move" Width="60" Height="20" Content="开始动画"/>
</Grid>
MatrixAnimationUsingPath
:使用矩阵变换来实现动画。
Storyboard.TargetName
:Storyboard
的附加依赖属性,设置实现动画的目标控件。Storyboard.TargetProperty
:Storyboard
的附加依赖属性,设置实现动画的具体属性。Duration
:从初始值到目标值的过程时间。PathGeometry
:路径几何对象,通过Figures
属性使用微语言来设置动画路径。MatrixAnimationUsingPath
不需要关心矩阵数据来源,会根据微语言路径自动获取,用起来还是很方便的。
示例
<Window.Resources>
<Storyboard x:Key="pathAnimation">
<MatrixAnimationUsingPath Storyboard.TargetName="mt"
Storyboard.TargetProperty="Matrix"
Duration="0:0:4">
<MatrixAnimationUsingPath.PathGeometry>
<PathGeometry Figures="M20,20 L100,100 a100,100,0,0,0,200,200"/>
</MatrixAnimationUsingPath.PathGeometry>
</MatrixAnimationUsingPath>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_Move">
<BeginStoryboard Storyboard="{StaticResource pathAnimation}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<Ellipse x:Name="ellipse" Width="30" Height="30" HorizontalAlignment="Left" VerticalAlignment="Top" Fill="Red">
<Ellipse.RenderTransform>
<MatrixTransform x:Name="mt"/>
</Ellipse.RenderTransform>
</Ellipse>
<Button Height="20" Width="50" Content="开始动画" Name="btn_Move"/>
</Grid>
所有的动画类型中,时常会用到的还有以下两个属性。
IsAddtive
:将目标属性的当前值添加到动画的起始值,也就是如果动画类型中设置了From
属性,那么每次会将目标属性的当前值加上From
设定的值后再进行动画。
IsCumulative
:如果动画不断重复,就累积动画值。这里的不断重复指的是在动画类型中设置了RepeatBehavior
属性(不能再Storyboard
中设置,否则无效)。
Completed
:整个故事板中的动画完成时触发。
CurrentGlobalSpeedInvalidated
:动画中的速度发生变化时触发,比如从静止到运动,运动到静止。
CurrentStateInvalidated
:状态变化,比如动画开始,动画结束。
CurretnTimeInvalidated
:时间线的变化时触发,也就是在运动过程中随着时间的行进一直触发。
RemoveRequested
:动画正被移除的时候触发
<Storyboard x:Key="sb_event"
Completed="Storyboard_Completed"
CurrentGlobalSpeedInvalidated="Storyboard_CurrentGlobalSpeedInvalidated"
CurrentTimeInvalidated="Storyboard_CurrentTimeInvalidated"
CurrentStateInvalidated="Storyboard_CurrentStateInvalidated">
<DoubleAnimation ....../>
</Storyboard>
BeginStoryboard
:开始一个故事板的动画。
Storyboard
:指定开始哪个故事板对象。Name
:BeginStoryborad
的对象名称,给后续操作进行关联。PauseStoryboard
:暂停动画。
BeginStoryboardName
:指定要暂停的是哪个BeginStoryboard
对象。ResumeStoryboard
:恢复动画。
BeginStoryboardName
:指定要暂停的是哪个BeginStoryboard
对象。StopStoryboard
:停止动画,让动画恢复原始状态。
BeginStoryboardName
:指定要暂停的是哪个BeginStoryboard
对象。SeekStoryboard
:跳转某一帧,某个时刻
BeginStoryboardName
:指定要暂停的是哪个BeginStoryboard
对象。Offset
:跳转到动画时间线上的那个时间点上,Offset="0:0:3"
。SetStoryboardSpeedRatio
:对动画进行加速或减速,有效值表示对动画速度的倍数,大于1为加速,大于0小于1为减速。
BeginStoryboardName
:指定要暂停的是哪个BeginStoryboard
对象。SpeedRatio
:速度倍数。从上面的各个操作对象也可以看出来,除了BeginStoryboard
需要指定故事板和自己的名字外,其他动画操作都是基于BeginStoryboard
对象来实行的。
<Window.Triggers>
<!--开始-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_1">
<BeginStoryboard Storyboard="{StaticResource sb}" Name="bsb"/>
</EventTrigger>
<!--暂停-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_2">
<PauseStoryboard BeginStoryboardName="bsb"/>
</EventTrigger>
<!--恢复-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_3">
<ResumeStoryboard BeginStoryboardName="bsb"/>
</EventTrigger>
<!--停止-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_4">
<StopStoryboard BeginStoryboardName="bsb"/>
</EventTrigger>
<!--跳转到某个时刻-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_5">
<SeekStoryboard BeginStoryboardName="bsb" Offset="0:0:3"/>
</EventTrigger>
<!--加速-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_6">
<SetStoryboardSpeedRatio BeginStoryboardName="bsb" SpeedRatio="3"/>
</EventTrigger>
<!--减速-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_7">
<SetStoryboardSpeedRatio BeginStoryboardName="bsb" SpeedRatio="0.3"/>
</EventTrigger>
</Window.Triggers>
除了使用EventTrigger
进行动画的操作外,还可以通过DataTrigger
的DataTrigger.EnterActions
与DataTrigger.ExitActions
属性来根据绑定数据的变化进行动画的设置。
DataTrigger.EnterActions
:用于定义当条件满足时的行为(动作的集合)。
DataTrigger.ExitActions
:用于定义当条件不满足时的行为。
这里所说的行为就是一系列动作的集合,而动作指的是任何派生自TriggerAction
类的对象,常为BeginStoryboard
、StopStoryboard
等动画操作动作。
示例
<Path Stroke="Transparent" StrokeThickness="8" StrokeDashArray="0.1 3" StrokeDashCap="Round" Opacity="0.8" x:Name="rf_Flow">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding RF_OnOffStatus}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="flowBegin">
<Storyboard RepeatBehavior="Forever" FillBehavior="Stop">
<ColorAnimation Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" To="WhiteSmoke" Duration="0:0:0.2"/>
<DoubleAnimation Storyboard.TargetProperty="StrokeDashOffset" From="0" To="-6"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="flowBegin"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
<Path.Data>
<GeometryGroup>
<LineGeometry StartPoint="30 0" EndPoint="20 216"/>
<LineGeometry StartPoint="70 0" EndPoint="70 216"/>
<LineGeometry StartPoint="110 0" EndPoint="110 216"/>
<LineGeometry StartPoint="150 0" EndPoint="150 216"/>
<LineGeometry StartPoint="190 0" EndPoint="190 216"/>
<LineGeometry StartPoint="230 0" EndPoint="240 216"/>
</GeometryGroup>
</Path.Data>
</Path>