总目录
前言
WPF提供了强大的动画功能,让我们开发者可以用其创建出比较酷炫的界面。本文为个人的学习总结,是一份关于WPF动画的详解文章。
一、动画概述
动画是快速循环播放一系列图像(其中每个图像与下一个图像略微不同)给人造成的一种幻觉,大脑感觉这组图像是一个变化的场景。 在电影中,摄像机每秒钟拍摄许多照片(帧),便可使人形成这种幻觉。 用投影仪播放这些帧时,观众便可以看电影了,计算机上的动画与此类似。
1、WPF中的动画类型
动画命名约定 | 类型 | 说明 |
---|---|---|
< 类型>Animation | 这些动画称为 “From/To/By”动画 或“基本”动画 | 它们在起始值和目标值之间进行动画处理, 或通过将偏移量值与其起始值相加来进行动画处理。 如常用的DoubleAnimation |
< 类型>AnimationUsingKeyFrames | 关键帧动画 | 功能比“From/To/By”动画的功能更强大, 因为可以指定任意多个目标值, 甚至可以控制它们的插值方法。 某些类型只能用关键帧动画进行动画处理 |
< 类型>AnimationUsingPath | 路径动画 | 路径动画支持使用几何路径来生成动画值。 |
< 类型>AnimationBase | 动画基类 | <类型>Animation 和 <类型>AnimationUsingKeyFrames 类的基类,用于创建自定义动画 |
2、Timeline时间线
所有动画类型均继承自 Timeline 类;因此,所有动画都是专用类型的时间线。详情可查动画和计时系统
它提供的属性让你可以指定该时间段的长度、开始时间、重复次数、该时间段内时间进度的快慢等。
Timeline类提供以下属性供我们制作动画时使用:
属性 | 作用 |
---|---|
Duration | 获取或设置此时间线播放的时间长度,而不是计数重复。 |
AutoReverse | 获取或设置一个值,该值指示时间线在完成向前迭代后是否按相反的顺序播放。 |
RepeatBehavior | 获取或设置此时间线的重复行为。 |
FillBehavior | 获取或设置一个值,该值指定 Timeline 在到达其有效期末尾后的行为。 |
AccelerationRatio | 获取或设置一个值,该值指定在将时间消逝从零加速到其最大速率的过程中所占用时间线的 Duration 的百分比。 |
DecelerationRatio | 获取或设置一个值,该值指定在将时间消逝从其最大速率减速到零的过程中所占用时间线的 Duration 的百分比。 |
SpeedRatio | 获取或设置此 Timeline 的时间相对于其父级的前进速率。 |
BeginTime | 获取或设置此 Timeline 应开始的时间。 |
Name | 获取或设置此 Timeline 的名称。 |
DesiredFrameRate | 获取或设置此时间线及其子时间线所需的帧速率。以上属性均为依赖属性,这个为附加属性 |
如我们定义一个基本动画DoubleAnimation:
<DoubleAnimation
Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:5"
AutoReverse="True" RepeatBehavior="Forever" />
-
Duration
Duration赋值格式为 :小时:分钟:秒
,如Duration="0:0:5"
表示动画有5s的时间,代码中指定Duration则通过TimeSpan,如xxxAnimation.Duration=new Duration(TimeSpan.FromSeconds(5));
如果动画没有指定Duration,将使用默认值1秒 -
AutoReverse
AutoReverse 属性指定时间线在到达 Duration 的终点后是否倒退。 如果将此动画属性设置为 true,则动画在到达 Duration 的终点后将倒退,即从其终止值向其起始值反向播放。 默认情况下,此属性为 false。如一个动画时顺时针旋转,那么设置了AutoReverse后,则会先顺时针旋转,然后逆时针旋转。 -
RepeatBehavior
RepeatBehavior 属性指定时间线的播放次数。 默认情况下,时间线的迭代次数为 1.0,即播放一次时间线,根本不进行重复。但是我们可以设置为Forever,表示一直重复播放 -
SpeedRatio
可理解为动画的速度,值大于0,值越大,动画速度越快 -
AccelerationRatio 和 DecelerationRatio
AccelerationRatio 可理解为动画前面加速部分的时间占比,
DecelerationRatio 可理解为动画结束前减速部分的时间占比;
- AccelerationRatio 和 DecelerationRatio的值是一个Double类型介于 0 和 1 之间的值(包括 0 和 1)
- 并且如果同时设置了AccelerationRatio 和 DecelerationRatio,则DecelerationRatio 和 AccelerationRatio 之和必须小于等于 1
- 使用 DecelerationRatio 属性创建动画,在停止之前减慢速度。
- 使用 AccelerationRatio 属性创建动画,在开始之后加快速度。
- 结合使用 AccelerationRatio 和 DecelerationRatio 属性创建动画,以缓慢启动、加快速度,然后在完成之前再次减慢速度。可用于创建轻松的效果或使运动看起来更自然。
- FillBehavior
FillBehavior 有HoldEnd 和Stop 两个枚举值,默认为HoldEnd 表示当动画结束后,保持动画结束时的状态,Stop表示动画结束后,回归动画原始状态
3、Storyboard 演示图版
1 基本信息
- Storyboard 是一种容器时间线,它为其包含的时间线提供目标信息( TargetName 和 TargetProperty)。
- 情节提要可以包含任意类型的 Timeline,包括其他容器时间线和动画。
- 可以使用 Storyboard 对象将影响各种对象和属性的时间线组合成一个时间线树,以便于组织和控制复杂的计时行为
2 Storyboard的使用
- 若要使用 Storyboard 组织和应用动画,可以将动画添加为 Storyboard 的子时间线。 Storyboard 类提供 Storyboard.TargetName 和 Storyboard.TargetProperty 附加属性。 可以在动画上设置这些属性以指定其目标对象和属性。
- 若要将动画应用于其目标,可以使用触发器操作或方法来开始 Storyboard。 在 XAML 中,将 BeginStoryboard 对象与 EventTrigger、Trigger 或 DataTrigger 一起使用。 在代码中,还可以使用 Begin 方法。
① 在xaml的EventTrigger中使用Storyboard
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Microsoft.SDK.Animation.ControllableStoryboardExample"
WindowTitle="Fading Rectangle Example">
<StackPanel Margin="10">
<Rectangle
Name="MyRectangle"
Width="100"
Height="100"
Fill="Blue">
</Rectangle>
<Button Name="BeginButton">Begin</Button>
<Button Name="PauseButton">Pause</Button>
<Button Name="ResumeButton">Resume</Button>
<Button Name="SkipToFillButton">Skip To Fill</Button>
<Button Name="StopButton">Stop</Button>
<StackPanel.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="BeginButton">
<BeginStoryboard Name="MyBeginStoryboard">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="(Rectangle.Opacity)"
From="1.0" To="0.0" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Button.Click" SourceName="PauseButton">
<PauseStoryboard BeginStoryboardName="MyBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.Click" SourceName="ResumeButton">
<ResumeStoryboard BeginStoryboardName="MyBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.Click" SourceName="SkipToFillButton">
<SkipStoryboardToFill BeginStoryboardName="MyBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.Click" SourceName="StopButton">
<StopStoryboard BeginStoryboardName="MyBeginStoryboard" />
</EventTrigger>
</StackPanel.Triggers>
</StackPanel>
</Page>
② 在xaml的Trigger中使用Storyboard,使用 Trigger 在属性值发生更改时启动 Storyboard。
以下示例在 IsMouseOver 属性变为 true 时使用 Trigger 对 Button 的 Opacity 进行动画处理。
<!-- PropertyTriggerExample.xaml
Shows how to use property triggers to start animations. -->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowTitle="Animate Properties with Storyboards">
<Page.Resources>
<Style x:Key="PropertyTriggerExampleButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Opacity" Value="0.25" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.25" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Page.Resources>
<StackPanel Margin="20">
<Button Style="{StaticResource PropertyTriggerExampleButtonStyle}">
Move the mouse over me.
</Button>
</StackPanel>
</Page>
属性 Trigger 对象所应用的动画的行为比 EventTrigger 动画或使用 Storyboard 方法启动的动画的行为更复杂。 它们在“切换”时使用的是其他 Trigger 对象定义的动画,但是在编写时使用 EventTrigger 和方法触发的动画
③ 在代码中使用Storyboard
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace SDKSample
{
public class ControllableStoryboardExample : Page
{
private Storyboard myStoryboard;
public ControllableStoryboardExample()
{
// Create a name scope for the page.
NameScope.SetNameScope(this, new NameScope());
this.WindowTitle = "Controllable Storyboard Example";
StackPanel myStackPanel = new StackPanel();
myStackPanel.Margin = new Thickness(10);
// Create a rectangle.
Rectangle myRectangle = new Rectangle();
myRectangle.Name = "myRectangle";
// Assign the rectangle a name by
// registering it with the page, so that
// it can be targeted by storyboard
// animations.
this.RegisterName(myRectangle.Name, myRectangle);
myRectangle.Width = 100;
myRectangle.Height = 100;
myRectangle.Fill = Brushes.Blue;
myStackPanel.Children.Add(myRectangle);
//
// Create an animation and a storyboard to animate the
// rectangle.
//
DoubleAnimation myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = 1.0;
myDoubleAnimation.To = 0.0;
myDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(5000));
myDoubleAnimation.AutoReverse = true;
// Create the storyboard.
myStoryboard = new Storyboard();
myStoryboard.Children.Add(myDoubleAnimation);
Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(Rectangle.OpacityProperty));
//
// Create some buttons to control the storyboard
// and a panel to contain them.
//
StackPanel buttonPanel = new StackPanel();
buttonPanel.Orientation = Orientation.Horizontal;
Button beginButton = new Button();
beginButton.Content = "Begin";
beginButton.Click += new RoutedEventHandler(beginButton_Clicked);
buttonPanel.Children.Add(beginButton);
Button pauseButton = new Button();
pauseButton.Content = "Pause";
pauseButton.Click += new RoutedEventHandler(pauseButton_Clicked);
buttonPanel.Children.Add(pauseButton);
Button resumeButton = new Button();
resumeButton.Content = "Resume";
resumeButton.Click += new RoutedEventHandler(resumeButton_Clicked);
buttonPanel.Children.Add(resumeButton);
Button skipToFillButton = new Button();
skipToFillButton.Content = "Skip to Fill";
skipToFillButton.Click += new RoutedEventHandler(skipToFillButton_Clicked);
buttonPanel.Children.Add(skipToFillButton);
Button setSpeedRatioButton = new Button();
setSpeedRatioButton.Content = "Triple Speed";
setSpeedRatioButton.Click += new RoutedEventHandler(setSpeedRatioButton_Clicked);
buttonPanel.Children.Add(setSpeedRatioButton);
Button stopButton = new Button();
stopButton.Content = "Stop";
stopButton.Click += new RoutedEventHandler(stopButton_Clicked);
buttonPanel.Children.Add(stopButton);
myStackPanel.Children.Add(buttonPanel);
this.Content = myStackPanel;
}
// Begins the storyboard.
private void beginButton_Clicked(object sender, RoutedEventArgs args)
{
// Specifying "true" as the second Begin parameter
// makes this storyboard controllable.
myStoryboard.Begin(this, true);
}
// Pauses the storyboard.
private void pauseButton_Clicked(object sender, RoutedEventArgs args)
{
myStoryboard.Pause(this);
}
// Resumes the storyboard.
private void resumeButton_Clicked(object sender, RoutedEventArgs args)
{
myStoryboard.Resume(this);
}
// Advances the storyboard to its fill period.
private void skipToFillButton_Clicked(object sender, RoutedEventArgs args)
{
myStoryboard.SkipToFill(this);
}
// Updates the storyboard's speed.
private void setSpeedRatioButton_Clicked(object sender, RoutedEventArgs args)
{
// Makes the storyboard progress three times as fast as normal.
myStoryboard.SetSpeedRatio(this, 3);
}
// Stops the storyboard.
private void stopButton_Clicked(object sender, RoutedEventArgs args)
{
myStoryboard.Stop(this);
}
}
}
3 Storyborad中的事件
- Completed:当此时间线完全播放完毕时发生:它将不再进入其活动周期。
- CurrentGlobalSpeedInvalidated
- CurrentStateInvalidated
- CurrentTimeInvalidated
- RemoveRequested
4、实现动画的要求
若要使属性具有动画功能,属性必须满足以下三个要求
- 它必须是依赖属性。
- 它必须属于派生自 DependencyObject 并实现 IAnimatable 接口的类。
- 必须存在可用的兼容动画类型。 如果 WPF 不提供,则可以创建自定义动画。如Color属性对应ColorAnimation
WPF 包含许多具有 IAnimatable 属性的对象。 控件(如 Button 和 TabControl)以及 Panel 和 Shape 对象继承自 DependencyObject。 其中大多数属性都是依赖属性。几乎可以在任何地方使用动画,包括在样式和控件模板中使用。
5、实现动画的大体步骤
- ① 创建动画,如DoubleAnimation
<DoubleAnimation From="1.0" To="0.0" Duration="0:0:5" AutoReverse="True" RepeatBehavior="Forever"/>
- ② 创建演示图版Storyboard并使用 TargetName 和 TargetProperty 附加属性指定要进行动画处理的对象和属性。
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:5"
AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
- ③ 将演示图版与触发器EventTrigger 关联
可以通过EventTrigger 的RoutedEvent设置触发动画的路由事件,SourceName 设置触发动画的控件名称
<Rectangle
Name="MyRectangle"
Width="100"
Height="100"
Fill="Blue">
<Rectangle.Triggers>
<!-- Animates the rectangle's opacity. -->
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:5"
AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
二、“From/To/By”动画
1、什么是 From/To/By 动画
From/To/By 动画是一种 AnimationTimeline 类型,可在起始值和终止值之间创建转换。 完成转换所需的时间取决于该动画的 Duration。
From/To/By 动画的目标值不能超过两个。 如果需要具有两个以上目标值的动画,请使用关键帧。
2、From/To/By 动画类型
由于动画会生成属性值,因此不同的属性类型具有不同的动画类型。 要为采用 Double 的属性(例如元素的 Width 属性)进行动画处理,可以使用生成 Double 值的动画。 要为采用 Point 的属性进行动画处理,请使用生成 Point 值的动画,依此类推。
From/To/By 动画类属于 System.Windows.Media.Animation 命名空间,并使用以下命名约定:<Type>Animation
其中 <Type>
为该类进行动画处理的值的类型。
WPF 提供以下 From/To/By 动画类(加粗字体表示该类相对来说是常用动画类型):
属性类型 | 对应的 From/To/By 动画类 |
---|---|
Byte | ByteAnimation |
Color | ColorAnimation |
Decimal | DecimalAnimation |
Double | DoubleAnimation |
Int16 | Int16Animation |
Int32 | Int32Animation |
Int64 | Int64Animation |
Point | PointAnimation |
Quaternion | QuaternionAnimation |
Rect | RectAnimation |
Rotation3D | Rotation3DAnimation |
Single | SingleAnimation |
Size | SizeAnimation |
Thickness | ThicknessAnimation |
Vector3D | Vector3DAnimation |
Vector | VectorAnimation |
3、From/To/By属性的设置
属性 | 说明 |
---|---|
From | 要显式指定动画起始值时,请使用 From 属性。 可以单独使用 From 属性,也可以与 To 或 By 属性结合使用。 如果仅指定 From 属性,则动画从该值转换到动画处理属性的基值。 |
To | 若要指定动画的终止值,请设置其 To 属性。 如果单独使用 To 属性,则动画从要进行动画处理的属性中获取其起始值,或者从应用到相同属性的另一个动画的输出中获取其起始值。 可以将 To 属性与 From 属性一起使用,以显式指定动画的起始值和终止值。 |
By | 使用 By 属性,可以为动画指定偏移,而不是显式的起始或终止值。 动画的 By 属性指定动画在其持续时间内更改某个值的程度。 可以单独使用 By 属性,或者与 From 属性结合使用。 如果仅指定 By 属性,动画会将偏移值添加到属性的基值或另一个动画的输出。 |
- 1 如果同时设置动画的 To 和 By 属性,则会忽略 By 属性。
- 2 设置了By属性,相当于间接设置了To的属性( To值=From值+By值 或 To值=默认值+By值 )
假如原始值或From属性值设置为100,By属性值设置为200,那么相当隐式设置了To为300,那么动画就是100-300之间的动态变化。
4、定义动画的两种方式
- 第一种方式:在控件内部定义动画
<Rectangle Name="MyRectangle" Width="100" Height="100" Fill="Blue" Margin="10">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="MouseLeftButtonDown" SourceName="MyRectangle">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="Width"
From="100" To="220" Duration="0:0:2"
AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
- 在资源中定义动画,在窗体的Triggers中定义触发器
<Window.Resources>
<!--1 首先在Window.Resources定义动画和演示图版-->
<Storyboard x:Key="myAnimation">
<DoubleAnimation Storyboard.TargetName="rect"
Storyboard.TargetProperty="Width"
By="200" Duration="0:0:2"
AutoReverse="True" RepeatBehavior="Forever"/>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<!--2 再者在Window.Triggers定义触发器以及关联触发器与演示图版-->
<EventTrigger RoutedEvent="MouseLeftButtonDown" SourceName="rect">
<BeginStoryboard Storyboard="{StaticResource myAnimation}"/>
</EventTrigger>
</Window.Triggers>
<StackPanel>
<Rectangle Name="rect" Width="100" Height="100" Fill="Green" Margin="10"/>
</StackPanel>
- 使用动画的时候,我们可以将xxxAnimation动画定义在控件内部,也可以定义在资源。
推荐使用第二种方式,该种方式对于动画资源的管理更合理 - 设置By就是设置偏移量,如上第二种方式,是一个针对Rectangle的Width属性动画,该Width 原始值为100,当我们动画中设置By=200的时候,则表示Width 需要从原始的100 动态变化到300(100加上200的偏移值等于300).
三、关键帧动画
1、什么是关键帧动画
- 与 From/To/By 动画类似,关键帧动画对目标属性的值进行动画处理。 它通过其 Duration 在目标值之间创建过渡。
- 但是,From/To/By 动画可以在两个值之间创建过渡,而单个关键帧动画可以在任意数量的目标值之间创建过渡。
- 关键帧动画的目标值使用关键帧对象进行描述,因此称作“关键帧动画”。
- 某些关键帧方法除支持多个目标值外,甚至还支持多个内插方法。 动画的内插方法定义了从一个值过渡到下一个值的方式。 有三种内插类型:离散、线性和曲线。
2、关键帧动画类型
关键帧动画类属于 System.Windows.Media.Animation 命名空间,并使用以下命名约定:<Type>AnimationUsingKeyFrames
其中 <Type>
为该类进行动画处理的值的类型。
WPF 提供了以下关键帧动画类:
属性类型 | 对应的关键帧动画类 | 支持的内插方法 |
---|---|---|
Boolean | BooleanAnimationUsingKeyFrames | 离散 |
Byte | ByteAnimationUsingKeyFrames | 离散、线性、曲线 |
Color | ColorAnimationUsingKeyFrames | 离散、线性、曲线 |
Decimal | DecimalAnimationUsingKeyFrames | 离散、线性、曲线 |
Double | DoubleAnimationUsingKeyFrames | 离散、线性、曲线 |
Int16 | Int16AnimationUsingKeyFrames | 离散、线性、曲线 |
Int32 | Int32AnimationUsingKeyFrames | 离散、线性、曲线 |
Int64 | Int64AnimationUsingKeyFrames | 离散、线性、曲线 |
Matrix | MatrixAnimationUsingKeyFrames | 离散 |
Object | ObjectAnimationUsingKeyFrames | 离散 |
Point | PointAnimationUsingKeyFrames | 离散、线性、曲线 |
Quaternion | QuaternionAnimationUsingKeyFrames | 离散、线性、曲线 |
Rect | RectAnimationUsingKeyFrames | 离散、线性、曲线 |
Rotation3D | Rotation3DAnimationUsingKeyFrames | 离散、线性、曲线 |
Single | SingleAnimationUsingKeyFrames | 离散、线性、曲线 |
String | StringAnimationUsingKeyFrames | 离散 |
Size | SizeAnimationUsingKeyFrames | 离散、线性、曲线 |
Thickness | ThicknessAnimationUsingKeyFrames | 离散、线性、曲线 |
Vector3D | Vector3DAnimationUsingKeyFrames | 离散、线性、曲线 |
Vector | VectorAnimationUsingKeyFrames | 离散、线性、曲线 |
3、关键帧和关键时间
就像在对不同属性类型进行动画处理时有不同类型的关键帧动画一样,关键帧对象的类型也各不相同:对于每种进行动画处理的值和所支持的内插方法,都有一个对象类型。 关键帧类型遵循以下命名约定:
<InterpolationMethod><Type>KeyFrame
其中 <InterpolationMethod> 是关键帧使用的内插方法,<Type> 是类进行动画处理的值的类型。 支持所有三种内插方法的关键帧动画有三种关键帧类型可供使用。 例如,可以针对 DoubleAnimationUsingKeyFrames 使用三种关键帧类型:DiscreteDoubleKeyFrame、LinearDoubleKeyFrame 和 SplineDoubleKeyFrame。
- 关键帧的主要目的是指定 KeyTime 和 Value。 每个关键帧类型都可提供这两种属性。
- Value 属性指定关键帧的目标值。
- KeyTime 属性指定何时(在动画的 Duration 内)到达关键帧的 Value。
- 关键帧动画开始后,会按关键帧的 KeyTime 属性定义的顺序来循环访问其关键帧
- 如果动画的 Duration 为 Automatic 或其 Duration 等于最后一个关键帧的时间,则动画结束。
- 如果动画的 Duration 大于最后一个关键帧的关键时间,则动画的关键帧值将一直保留,直到到达其 Duration 的末尾为止。
下面是一个关键帧动画的简单使用案例:
<Rectangle Fill="Blue" Width="100" Height="100" HorizontalAlignment="Left">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="tt" X="0" Y="0" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<!--关键帧动画定义代码-->
<DoubleAnimationUsingKeyFrames Duration="0:0:10"
Storyboard.TargetName="tt"
Storyboard.TargetProperty="X">
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0" />
<LinearDoubleKeyFrame Value="350" KeyTime="0:0:2" />
<LinearDoubleKeyFrame Value="50" KeyTime="0:0:7" />
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:8" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
分析一下代码:
<DoubleAnimationUsingKeyFrames Duration="0:0:10" Storyboard.TargetName="tt" Storyboard.TargetProperty="X">
<!--第一关键帧:立即将动画的输出值设置为0-->
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0" />
<!--第二个关键帧在 0 和 350 之间进行动画处理。 它在第一个关键帧结束后开始(开始时间 = 0 秒),播放 2 秒钟,结束时间 = 0:0:2。-->
<LinearDoubleKeyFrame Value="350" KeyTime="0:0:2" />
<!--第三个关键帧在 350 和 50 之间进行动画处理。 它在第二个关键帧结束时开始(开始时间 = 2 秒),播放 5 秒钟,结束时间 = 0:0:7。-->
<LinearDoubleKeyFrame Value="50" KeyTime="0:0:7" />
<!--第四个关键帧在 50 和 200 之间进行动画处理。 它在第三个关键帧结束时开始(开始时间 = 7 秒),播放 1 秒钟,结束时间 = 0:0:8。-->
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:8" />
<!--由于动画的 Duration 属性已设置为 10 秒,所以该动画保留其最终值两秒钟,在时间 = 0:0:10 时结束。-->
</DoubleAnimationUsingKeyFrames>
4、内插方法
某些关键帧动画支持多种内插方法,动画的内插对动画在其持续时间内在值之间进行过渡的方式进行了描述。例如,可以针对 DoubleAnimationUsingKeyFrames 使用三种关键帧类型:DiscreteDoubleKeyFrame、LinearDoubleKeyFrame 和 SplineDoubleKeyFrame。
1 线性内插(对应线性关键帧)
使用线性内插,动画将以固定速度连贯的进行播放
如上面的案例使用LinearDoubleKeyFrame 就是线性内插,动画是均匀且连贯的
2 离散内插 (对应离散关键帧)
使用离散内插,动画将是跳跃性的,不连贯的。
<Rectangle Fill="Blue" Width="100" Height="100" HorizontalAlignment="Left">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="tt" X="0" Y="0" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Duration="0:0:4"
Storyboard.TargetName="tt"
Storyboard.TargetProperty="X">
<DiscreteDoubleKeyFrame Value="0" KeyTime="0:0:0" />
<DiscreteDoubleKeyFrame Value="350" KeyTime="0:0:1" />
<DiscreteDoubleKeyFrame Value="50" KeyTime="0:0:2" />
<DiscreteDoubleKeyFrame Value="200" KeyTime="0:0:3" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
3 曲线内插(对应曲线关键帧)
使用曲线内插,可以让动画有加速或减速的效果
- 曲线内插可用于达到更现实的计时效果。 由于动画通常用于模拟现实世界中出现的效果,因此开发人员可能需要精确地控制对象的加速和减速,并需要严格地对计时段进行操作。
- 与线性关键帧和离散关键帧不同的是使用曲线关键帧多了一个KeySpline属性,该属性是定义曲线关键帧动画的关键属性,决定了动画的加速与减速,如下所示:
<!--线性关键帧-->
<LinearDoubleKeyFrame Value="350" KeyTime="0:0:2" />
<!--离散关键帧-->
<DiscreteDoubleKeyFrame Value="200" KeyTime="0:0:3" />
<!--曲线关键帧 关键属性:KeySpline-->
<SplineDoubleKeyFrame Value="500" KeyTime="0:0:7" KeySpline="0.0,1.0 1.0,0.0" />
曲线关键帧中的曲线:是一条三次方贝塞尔曲线,而该曲线是由一个起点,一个终点,两个控制点定义。
绘制曲线关键帧的KeySpline属性:定义了从(0,0)延申到(1,1)的贝塞尔曲线的两个控制点。
- 第一个控制点控制贝塞尔曲线前半部分的曲线因子,第二个控制点控制贝塞尔线段后半部分的曲线因子。
- 生成的曲线描述了该自由绘制曲线关键帧的变化率。
- 曲线陡度越大,关键帧更改其值的速度越快。 曲线趋于平缓时,关键帧更改其值的速度也趋于缓慢。
下图展示了两个贝塞尔曲线,以供加深贝塞尔曲线的了解
下面是一个曲线关键帧的简单使用案例:
<Rectangle Fill="Blue" Width="100" Height="100" HorizontalAlignment="Left">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="tt" X="0" Y="0" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Duration="0:0:8" RepeatBehavior="Forever"
Storyboard.TargetName="tt" Storyboard.TargetProperty="X">
<SplineDoubleKeyFrame Value="500" KeyTime="0:0:3" KeySpline="0.0,1.0 1.0,0.0" />
<SplineDoubleKeyFrame Value="200" KeyTime="0:0:5" KeySpline="0.0,0.0 1.0,0.0" />
<SplineDoubleKeyFrame Value="350" KeyTime="0:0:8" KeySpline="0.25,0.5 0.75,1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
4 组合内插(混合使用多种关键帧)
可在一个关键帧动画中使用具有不同内插类型的关键帧。 如果两个具有不同内插的关键帧动画彼此跟随,第二个关键帧的内插方法将用于创建从第一个值到第二个值的过渡。
<Rectangle Fill="Blue" Width="100" Height="100" HorizontalAlignment="Left">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="tt" X="0" Y="0" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Duration="0:0:7" RepeatBehavior="Forever"
Storyboard.TargetName="tt" Storyboard.TargetProperty="X">
<DiscreteDoubleKeyFrame Value="500" KeyTime="0:0:2" />
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:4" />
<SplineDoubleKeyFrame Value="350" KeyTime="0:0:6" KeySpline="0.25,0.5 0.75,1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
5、KeyTime的使用
① 每个关键帧的 KeyTime 指定该关键帧的结束时间。
② 关键时间的指定方式有:时间值(TimeSpan),百分比,特殊值Uniform或Paced
③ 一个关键帧动画中的不同关键帧可使用不同的关键时间类型。
- 时间值(TimeSpan)
<DoubleAnimationUsingKeyFrames Duration="0:0:10" RepeatBehavior="Forever" Storyboard.TargetName="tt" Storyboard.TargetProperty="X">
<LinearDoubleKeyFrame Value="100" KeyTime="0:0:3" />
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:8" />
<LinearDoubleKeyFrame Value="500" KeyTime="0:0:9" />
<LinearDoubleKeyFrame Value="600" KeyTime="0:0:10" />
</DoubleAnimationUsingKeyFrames>
- 百分比:百分比值指定关键帧在动画的 Duration 内的某百分比处结束
<DoubleAnimationUsingKeyFrames Duration="0:0:10" RepeatBehavior="Forever" Storyboard.TargetName="tt" Storyboard.TargetProperty="X">
<!--在前 3 秒钟内,第一个关键帧将在基值和 100 之间进行动画处理,结束时间 = 0:0:3。-->
<LinearDoubleKeyFrame Value="100" KeyTime="30%" />
<!--第二个关键帧在 100 和 200 之间进行动画处理。
它在第一个关键帧结束后开始(开始时间 = 3 秒),播放 5 秒钟,结束时间 = 0:0:8 (0.8 * 10 = 8)。-->
<LinearDoubleKeyFrame Value="200" KeyTime="80%" />
<!--第三个关键帧在 200 和 500 之间进行动画处理。
它在第二个关键帧结束时开始(开始时间 = 8 秒),播放 1 秒钟,结束时间 = 0:0:9 (0.9 * 10 = 9)。-->
<LinearDoubleKeyFrame Value="500" KeyTime="90%" />
<!--第四个关键帧在 500 和 600 之间进行动画处理。
它在第三个关键帧结束时开始(开始时间 = 9 秒),播放 1 秒钟,结束时间 = 0:0:10 (1 * 10 = 10)。-->
<LinearDoubleKeyFrame Value="600" KeyTime="100%" />
</DoubleAnimationUsingKeyFrames>
- 特殊值Uniform:如果希望每个关键帧的持续时间都相同,应使用 Uniform 计时。
<DoubleAnimationUsingKeyFrames Duration="0:0:10" RepeatBehavior="Forever" Storyboard.TargetName="tt" Storyboard.TargetProperty="X">
<LinearDoubleKeyFrame Value="100" KeyTime="Uniform" />
<LinearDoubleKeyFrame Value="200" KeyTime="Uniform" />
<LinearDoubleKeyFrame Value="500" KeyTime="Uniform" />
<LinearDoubleKeyFrame Value="600" KeyTime="Uniform" />
</DoubleAnimationUsingKeyFrames>
- 特殊值Paced:如果希望以固定速率进行动画处理,应使用 Paced 计时。
<DoubleAnimationUsingKeyFrames Duration="0:0:10" RepeatBehavior="Forever" Storyboard.TargetName="tt" Storyboard.TargetProperty="X">
<LinearDoubleKeyFrame Value="100" KeyTime="Paced" />
<LinearDoubleKeyFrame Value="200" KeyTime="Paced" />
<LinearDoubleKeyFrame Value="500" KeyTime="Paced" />
<LinearDoubleKeyFrame Value="600" KeyTime="Paced" />
</DoubleAnimationUsingKeyFrames>
四、缓动函数
1、什么是缓动函数
缓动函数允许将自定义的数学公式应用到动画。 例如,用户可能希望某个对象逼真地弹跳或表现出像在弹簧上一样。 可使用关键帧甚至 From/To/By 动画来近似地实现这些效果,但它将需要大量工作,并且动画不如使用数学公式准确。
可以将缓动函数理解为已经封装好的准确的动画效果
2、缓动函数类型
- 运行时提供的缓动函数
- 自定义缓动函数,通过从 EasingFunctionBase 继承来创建自己的缓动函数
下表列举了运行时提供的缓动函数
缓动函数 | 说明 |
---|---|
BackEase | 动画开始在指定路径上运动前稍微收缩动画的运行。 |
BounceEase | 创建弹跳效果。 |
CircleEase | 使用圆函数创建加速和/或减速的动画。 |
CubicEase | 使用公式 f(t) = t3 创建加速和/或减速的动画。 |
ElasticEase | 创建一个动画,模拟弹簧的来回振荡运动,直到它达到停止状态。 |
ExponentialEase | 使用指数公式创建加速和/或减速的动画。 |
PowerEase | 使用公式 f(t) = tp 创建加速和/或减速的动画,其中 p 等于 Power 属性。 |
QuadraticEase | 使用公式 f(t) = t2 创建加速和/或减速的动画。 |
QuarticEase | 使用公式 f(t) = t4 创建加速和/或减速的动画。 |
QuinticEase | 使用公式 f(t) = t5 创建加速和/或减速的动画。 |
SineEase | 使用正弦公式创建加速和/或减速的动画。 |
3、使用缓动函数
具体详情,可查看缓动函数
- 缓动函数应用于 From/To/By 动画
<Ellipse Name="ellipse" Width="100" Height="100" Fill="Green">
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Ellipse.MouseDown">
<BeginStoryboard>
<Storyboard>
<Storyboard x:Name="myStoryboard">
<ThicknessAnimation From="30" To="300" Duration="00:00:3" Storyboard.TargetProperty="Margin">
<ThicknessAnimation.EasingFunction>
<BounceEase Bounces="2" EasingMode="EaseOut" Bounciness="2" />
</ThicknessAnimation.EasingFunction>
</ThicknessAnimation>
</Storyboard>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Ellipse.Triggers>
</Ellipse>
- 缓动函数应用于关键帧动画
<Rectangle Name="myRectangle" Width="200" Height="200" Fill="Blue">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Height" Storyboard.TargetName="myRectangle">
<EasingDoubleKeyFrame Value="30" KeyTime="00:00:02">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
<EasingDoubleKeyFrame Value="200" KeyTime="00:00:06">
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase Bounces="5" EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
4、EasingMode的使用
可以使用 EasingMode 属性更改缓动函数的行为方式,即更改动画的内插方式。
可为 EasingMode 赋予三个可能的值:
- EaseIn:内插时使用与缓动函数关联的数学公式。
- EaseOut:内插时使用 100% 内插值减去与缓动函数关联的公式输出结果之后的值。
- EaseInOut:对于第一部分动画,内插时使用 EaseIn,对于第二部分动画,内插时使用 EaseOut。
5、自定义缓动函数
- 自定义缓动函数
namespace CustomEasingFunction
{
public class CustomSeventhPowerEasingFunction : EasingFunctionBase
{
public CustomSeventhPowerEasingFunction()
: base()
{
}
// 通过重写EaseInCore方法,为缓动函数指定自己的逻辑。请注意,这个逻辑适用于“EaseIn”模式的内插。
protected override double EaseInCore(double normalizedTime)
{
// applies the formula of time to the seventh power.
return Math.Pow(normalizedTime, 7);
}
protected override Freezable CreateInstanceCore()
{
return new CustomSeventhPowerEasingFunction();
}
}
}
- 使用自定义缓动函数
<Window x:Class="CustomEasingFunction.Window1"
xmlns:CustomEase="clr-namespace:CustomEasingFunction"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="500" Width="300">
<StackPanel>
<TextBlock Margin="10" TextWrapping="Wrap">Click on the rectangle to start the animation</TextBlock>
<StackPanel x:Name="LayoutRoot" Background="White">
<Rectangle Name="myRectangle" Width="200" Height="30" Fill="Blue">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="30" To="300" Duration="00:00:3"
Storyboard.TargetName="myRectangle"
Storyboard.TargetProperty="Height">
<DoubleAnimation.EasingFunction>
<CustomEase:CustomSeventhPowerEasingFunction EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</StackPanel>
</StackPanel>
</Window>
五、路径动画
1、什么是路径动画
- 路径动画可用于沿着复杂路径移动和旋转对象。
- 路径动画是一种使用 PathGeometry 作为输入的 AnimationTimeline。
2、路径动画的类型
路径动画类属于 System.Windows.Media.Animation 命名空间,并使用以下命名约定:<Type>AnimationUsingPath
其中 <Type> 为该类进行动画处理的值的类型。
WPF 提供以下路径动画类。
属性类型 | 相应的路径动画类 | 说明 |
---|---|---|
Double | DoubleAnimationUsingPath | DoubleAnimationUsingPath 从其 PathGeometry 生成 Double 值。 通过设置 Source 属性,可以指定 DoubleAnimationUsingPath 是使用 x 坐标、y 坐标还是路径的角度作为其输出。 可以使用 DoubleAnimationUsingPath 来沿着 x 轴或 y 轴旋转或移动对象。 |
Matrix | MatrixAnimationUsingPath | MatrixAnimationUsingPath 从其 PathGeometry 生成 Matrix 值。 与 MatrixTransform 一起使用时,MatrixAnimationUsingPath 可以沿路径移动对象。 如果将 MatrixAnimationUsingPath 的 DoesRotateWithTangent 属性设置为 true,它还会沿路径曲线旋转对象。 |
Point | PointAnimationUsingPath | PointAnimationUsingPath 从其 PathGeometry 的 x 和 y 坐标生成 Point 值。 通过使用 PointAnimationUsingPath 对采用 Point 值的属性进行动画处理,可以沿路径移动对象。 PointAnimationUsingPath 不能旋转对象。 |
3、使用路径动画
1 使用DoubleAnimationUsingPath路径动画(双精度动画)(矩阵动画)(点动画)
- DoubleAnimationUsingPath 通过设置 Storyboard.TargetName和 Storyboard.TargetProperty 指定应用动画的对象和属性,
- 通过设置PathGeometry(Geometry类型)指定动画的路径
- 通过Source 可以指定是使用 x 坐标、y 坐标还是路径的角度作为其输出
<Window x:Class="WpfApp1.Views.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1.Views"
mc:Ignorable="d"
Title="Window1" Height="500" Width="1200">
<Window.Resources>
<PathGeometry x:Key="AnimationPath" PresentationOptions:Freeze="True" Figures="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100" />
</Window.Resources>
<Canvas Width="400" Height="400" Background="LightBlue">
<Path Data="{StaticResource AnimationPath}" Stroke="Green" StrokeThickness="3"></Path>
<Rectangle Width="50" Height="50" Fill="Blue">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="tt" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseDown">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingPath
Storyboard.TargetName="tt"
Storyboard.TargetProperty="X"
PathGeometry="{StaticResource AnimationPath}"
Source="X"
Duration="0:0:5" />
<DoubleAnimationUsingPath
Storyboard.TargetName="tt"
Storyboard.TargetProperty="Y"
PathGeometry="{StaticResource AnimationPath}"
Source="Y"
Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
</Window>
- 设置
Source="Angle"
时,则会使对象沿着路径的轮廓旋转(转动)。
设置Source="Angle"
的时候,也需要配合RotateTransform 一起使用
<Window x:Class="WpfApp1.Views.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1.Views"
mc:Ignorable="d"
Title="Window1" Height="500" Width="1200">
<Window.Resources>
<PathGeometry x:Key="AnimationPath" PresentationOptions:Freeze="True" Figures="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100" />
</Window.Resources>
<Canvas Width="400" Height="400" Background="LightBlue">
<Path Data="{StaticResource AnimationPath}" Stroke="Green" StrokeThickness="3"></Path>
<Rectangle Width="50" Height="50" Fill="Blue">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="rt"></RotateTransform>
<TranslateTransform x:Name="tt" />
</TransformGroup>
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseDown">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingPath
Storyboard.TargetName="rt"
Storyboard.TargetProperty="Angle"
PathGeometry="{StaticResource AnimationPath}"
Source="Angle"
Duration="0:0:5" />
<DoubleAnimationUsingPath
Storyboard.TargetName="tt"
Storyboard.TargetProperty="X"
PathGeometry="{StaticResource AnimationPath}"
Source="X"
Duration="0:0:5" />
<DoubleAnimationUsingPath
Storyboard.TargetName="tt"
Storyboard.TargetProperty="Y"
PathGeometry="{StaticResource AnimationPath}"
Source="Y"
Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
</Window>
2 使用 MatrixAnimationUsingPath 路径动画(矩阵动画)
通过执行以下操作沿着路径针对对象进行动画处理:
- 将 MatrixTransform 应用于对象,以便移动它。
- 使用 PathGeometry 定义路径。
- 创建 MatrixAnimationUsingPath 并使用其对 MatrixTransform 的 Matrix 属性进行动画处理。 MatrixAnimationUsingPath 采用 PathGeometry 并使用它来生成 Matrix 值。
<Window x:Class="WpfApp1.Views.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1.Views"
mc:Ignorable="d"
Title="Window1" Height="500" Width="800">
<Window.Resources>
<PathGeometry x:Key="AnimationPath" PresentationOptions:Freeze="True" Figures="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100" />
</Window.Resources>
<Canvas Width="400" Height="400">
<Path Data="{StaticResource AnimationPath}" Stroke="SeaGreen" StrokeThickness="3"></Path>
<Rectangle Width="50" Height="50" Fill="BlueViolet">
<Rectangle.RenderTransform>
<MatrixTransform x:Name="mt" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseDown">
<BeginStoryboard>
<Storyboard>
<MatrixAnimationUsingPath Duration="0:0:5" RepeatBehavior="Forever"
Storyboard.TargetName="mt" Storyboard.TargetProperty="Matrix"
PathGeometry="{StaticResource AnimationPath}"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
</Window>
矩阵动画IsOffsetCumulative的使用
- 当MatrixAnimationUsingPath 设置
IsOffsetCumulative="True"
时,动画矩阵的偏移量随着该动画重复而累积 - 由于偏移量累积,因此对象下一次的起点不再是重置为起始位置,而是偏移后的位置。
当设置IsOffsetCumulative="True"
时,动画效果如下:
矩阵动画DoesRotateWithTangent 的使用
- 使用 MatrixAnimationUsingPath 对象对 MatrixTransform 的 Matrix 属性进行动画处理。
- MatrixTransform 应用于对象,并使其沿着曲线路径移动。
- 当
DoesRotateWithTangent
属性设置为 true,矩形会沿着路径的切线旋转。
当 DoesRotateWithTangent
属性设置为 true,动画效果如下:
3 使用 PointAnimationUsingPath路径动画(点动画)
使用 PointAnimationUsingPath 对象沿着曲线路径针对 Point 进行动画处理。
<Window x:Class="WpfApp1.Views.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1.Views"
mc:Ignorable="d"
Title="Window1" Height="500" Width="800">
<Window.Resources>
<PathGeometry x:Key="AnimationPath" PresentationOptions:Freeze="True" Figures="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100" />
</Window.Resources>
<Canvas Width="400" Height="400" Background="LightBlue">
<Path Stroke="Red" Data="{StaticResource AnimationPath}" Margin="15"></Path>
<Path Fill="Blue" Margin="15,15,15,15">
<Path.Data>
<EllipseGeometry x:Name="MyAnimatedEllipseGeometry" Center="10,100" RadiusX="15" RadiusY="15" />
</Path.Data>
<Path.Triggers>
<EventTrigger RoutedEvent="Path.Loaded">
<BeginStoryboard Name="MyBeginStoryboard">
<Storyboard>
<PointAnimationUsingPath Duration="0:0:5" RepeatBehavior="Forever"
Storyboard.TargetName="MyAnimatedEllipseGeometry"
Storyboard.TargetProperty="Center" PathGeometry="{StaticResource AnimationPath}">
</PointAnimationUsingPath>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
</Path>
</Canvas>
</Window>
六、自定义动画
感兴趣可查自定义动画概述
结语
以上就是本文的内容,希望以上内容可以帮助到您,如文中有不对之处,还请批评指正。