自定义动画概述
本主题介绍如何以及何时通过以下方法来扩展 WPF 动画系统:创建自定义关键帧、动画类或者使用逐帧回调来绕过该系统。
先决条件
要了解本主题,您应当熟悉 WPF 提供的不同类型的动画。 有关更多信息,请参见 From/To/By 动画概述、关键帧动画概述和路径动画概述。
因为动画类继承自 Freezable 类,所以您应当熟悉 Freezable 对象以及如何继承 Freezable。 有关更多信息,请参见 Freezable 对象概述。
扩展动画系统
有多种方法来扩展 WPF 动画系统,具体取决于您要使用的内置功能的级别。 WPF 动画引擎中有三个主要的扩展性点:
通过继承某个 *<类型>*KeyFrame 类(如 DoubleKeyFrame)来创建一个自定义关键帧对象。 此方法使用 WPF 动画引擎的大部分内置功能。
通过继承 AnimationTimeline 或某个 *<类型>*AnimationBase 类来创建您自己的动画类。
使用逐帧回调来逐帧生成动画。 此方法完全绕过动画和计时系统。
下表介绍了扩展动画系统的一些方案。
要执行此操作... |
请使用此方法 |
---|---|
自定义具有对应 <类型>AnimationUsingKeyFrames 的类型的值之间的内插。 |
创建自定义关键帧。 有关更多信息,请参见创建自定义关键帧部分。 |
除了自定义具有对应 <类型>Animation 的类型的值之间的内插外,还要自定义其他内容。 |
创建继承自 <类型>AnimationBase 类的自定义动画类,该类对应于您要进行动画处理的类型。 有关更多信息,请参见创建自定义动画类部分。 |
对没有对应 WPF 动画的类型进行动画处理 |
使用 ObjectAnimationUsingKeyFrames 或创建继承自 AnimationTimeline 的类。 有关更多信息,请参见创建自定义动画类部分。 |
使用按每个帧计算并基于最后一组对象交互的值对多个对象进行动画处理 |
使用逐帧回调。 有关更多信息,请参见创建并使用逐帧回调部分。 |
创建一个自定义关键帧
创建自定义关键帧类是扩展动画系统的最简单的方法。 当您需要对关键帧动画使用一种不同的内插方法时,可使用此方法。 如关键帧动画概述中所述,关键帧动画使用关键帧对象来生成它的输出值。 每个关键帧对象都执行三个功能:
实现说明
派生自 *<类型>*KeyFrame 抽象类并实现 InterpolateValueCore 方法。 InterpolateValueCore 方法返回关键帧的当前值。 它采用两个参数:前一个关键帧的值以及 0 到 1 之间的进度值。 进度值为 0 表示关键帧刚刚启动,值为 1 表示关键帧刚刚完成,应返回它的 Value 属性所指定的值。
因为 *<类型>*KeyFrame 类继承自 Freezable 类,所以您还必须重写 CreateInstanceCore 核心来返回类的新实例。 如果该类未使用依赖项属性来存储其数据或者在创建后需要额外的初始化,则您可能还需要重写其他方法;有关更多信息,请参见Freezable 对象概述。
在创建自定义 *<类型>*KeyFrame 动画后,您可以将它与该类型的 *<类型>*AnimationUsingKeyFrames 一起使用。
创建自定义动画类
通过创建自己的动画类型,您可以更好地控制对象的动画方式。 有两种推荐方法来创建自己的动画类型:从 AnimationTimeline 类或 *<类型>*AnimationBase 类派生。 不推荐从 *<类型>*Animation 或 *<类型>*AnimationUsingKeyFrames 类派生。
从 <类型>AnimationBase 派生
从 *<类型>*AnimationBase 类派生是创建新的动画类型的最简单方法。 当您希望为已有对应 *<类型>*AnimationBase 类的类型创建新的动画时,可使用此方法。
实现说明
派生自 *<类型>*Animation 类并实现 GetCurrentValueCore 方法。 GetCurrentValueCore 方法返回动画的当前值。 它采用三个参数:建议的起始值、建议的结束值以及您用于确定动画进度的 AnimationClock。
因为 *<类型>*AnimationBase 类继承自 Freezable 类,所以您还必须重写 CreateInstanceCore 核心来返回类的新实例。 如果该类未使用依赖项属性来存储其数据或者在创建后需要额外的初始化,则您可能还需要重写其他方法;有关更多信息,请参见Freezable 对象概述。
有关更多信息,请参见您要对其进行动画处理的类型的 *<类型>*AnimationBase 类的 GetCurrentValueCore 方法文档。 有关示例,请参见 Custom Animation Sample(自定义动画示例)
其他方法
如果您仅仅希望更改动画值的内插方式,则应考虑从某个 *<类型>*KeyFrame 类派生。 您创建的关键帧可以与 WPF 所提供的对应 *<类型>*AnimationUsingKeyFrames 一起使用。
从 AnimationTimeline 派生
当您希望为还没有匹配的 WPF 动画的类型创建动画,或者希望创建非强类型的动画时,应当从 AnimationTimeline 类派生。
实现说明
从 AnimationTimeline 类派生并重写以下成员:
CreateInstanceCore – 如果新类是具体的,则必须重写 CreateInstanceCore 来返回类的新实例。
GetCurrentValue – 重写此方法以返回动画的当前值。 它采用三个参数:默认初始值、默认目标值以及 AnimationClock。 使用 AnimationClock 来获取动画的当前时间或进度。 您可以选择是否使用默认初始值和目标值。
IsDestinationDefault – 重写此属性来指示您的动画是否使用 GetCurrentValue 方法指定的默认目标值。
TargetPropertyType – 重写此属性来指示动画产生的输出的 Type。
如果该类未使用依赖项属性来存储其数据或者在创建后需要额外的初始化,则您可能还需要重写其他方法;有关更多信息,请参见Freezable 对象概述。
推荐的范例(由 WPF 动画使用)将使用两个继承级别:
创建一个派生自 AnimationTimeline 的抽象 *<类型>*AnimationBase 类。 此类应重写 TargetPropertyType 方法。 它还应引入一个新的抽象方法 GetCurrentValueCore 并重写 GetCurrentValue,以便验证默认初始值和默认目标值参数的类型,然后调用 GetCurrentValueCore。
创建另一个类,该类继承自新的 *<类型>*AnimationBase 类并重写 CreateInstanceCore 方法、您引入的 GetCurrentValueCore 方法以及 IsDestinationDefault 属性。
其他方法
如果您希望对没有对应 From/To/By 动画或关键帧动画的类型进行动画处理,应考虑使用 ObjectAnimationUsingKeyFrames。 因为它是弱类型,所以 ObjectAnimationUsingKeyFrames 可以对任何类型的值进行动画处理。 此方法的弱点是 ObjectAnimationUsingKeyFrames 仅支持离散内插。
使用逐帧回调
当需要完全绕过 WPF 动画系统时使用此方法。 此方法的一种方案是物理动画,在其中的每一个动画步骤,都需要基于最后一组对象交互重新计算动画对象的新方向或位置。
实现说明
与本概述中介绍的其他方法不同,要使用逐帧回调,不需要创建自定义动画或关键帧类,
而应注册需要进行动画处理的对象所在对象的 Rendering 事件。 每帧调用一次此事件处理程序方法。 每次 WPF 将可视化树中持久呈现的数据封送到组合树中时,都将调用事件处理程序方法。
在事件处理程序中,执行动画效果所需的任意计算,并设置需要使用这些值进行动画处理的对象的属性。
要获取当前帧的显示时间,可以将与此事件关联的 EventArgs 强制转换为 RenderingEventArgs,从而提供可用于获取当前帧呈现时间的 RenderingTime 属性。
有关更多信息,请参见 Rendering 页。