自定义动画概述

本主题介绍如何以及何时通过创建自定义关键帧或动画类,或使用每帧回调来绕过并扩展 WPF 动画系统。

先决条件

若要了解本主题,应熟悉 WPF 提供的不同类型的动画。 有关详细信息,请参阅 From/To/By 动画概述、Key-Frame 动画概述路径动画概述

由于动画类继承自 Freezable 类,因此应熟悉 Freezable 对象以及如何从 Freezable继承。 有关详细信息,请参阅 可冻结对象概述

扩展动画系统

可通过多种方式扩展 WPF 动画系统,具体取决于要使用的内置功能级别。 WPF 动画引擎中有三个主要扩展点:

  • 要创建自定义关键帧对象,可以从 *<类型* 的其中一个>KeyFrame 类继承,如 DoubleKeyFrame。 此方法使用 WPF 动画引擎的大部分内置功能。

  • 通过继承自 AnimationTimeline 或 *<类型>*AnimationBase 类之一来创建自己的动画类。

  • 使用每帧回调基于每个帧生成动画。 此方法完全绕过动画和计时系统。

下表介绍了扩展动画系统的一些方案。

当你想要... 使用此方法
在具有对应 *<类型>*AnimationUsingKeyFrames 的类型中自定义值之间的内插。 创建自定义关键帧。 有关详细信息,请参阅 创建自定义关键帧 部分。
不仅可以自定义插值的类型,还可以自定义具有相应 *<类型>*动画的其他方面。 创建一个自定义动画类,该类继承自 *<Type>*AnimationBase 类,该类对应于要进行动画处理的类型。 有关详细信息,请参阅 创建自定义动画类 部分。
对没有相应 WPF 动画的类型进行动画处理 使用 ObjectAnimationUsingKeyFrames 或创建继承自 AnimationTimeline的类。 有关详细信息,请参阅 创建自定义动画类 部分。
基于上一组对象交互,使用每帧计算的值为多个对象创建动画。 使用每帧回调。 有关详细信息,请参阅第 创建 Use Per-Frame Callback 节。

创建自定义关键帧

创建自定义关键帧类是扩展动画系统的最简单方法。 当您需要为关键帧动画选择不同的插值方法时,请使用此方法。 如 Key-Frame 动画概述中所述,关键帧动画使用关键帧对象生成其输出值。 每个关键帧对象执行三个函数:

  • 使用其 Value 属性指定目标值。

  • 指定应使用其 KeyTime 属性达到该值的具体时间。

  • 通过实现 InterpolateValueCore 方法,在其自身的值与上一个关键帧的值之间进行插值。

实施说明

从 *<类型>*KeyFrame 抽象类派生,并实现 InterpolateValueCore 方法。 InterpolateValueCore 方法返回关键帧的当前值。 它采用两个参数:上一个关键帧的值和介于 0 到 1 之间的进度值。 进度为 0 表示关键帧刚刚启动,值 1 表示关键帧刚刚完成,并且应返回由其 Value 属性指定的值。

由于 *<Type>*KeyFrame 类继承自 Freezable 类,因此还必须重写 CreateInstanceCore 核心以返回类的新实例。 如果类不使用依赖属性来存储其数据,或者需要在创建后进行额外的初始化,则可能需要重写其他方法;有关详细信息,请参阅 冻结对象概述

创建自定义 *<类型>*KeyFrame 动画后,可以将它与 *<类型>*AnimationUsingKeyFrames 用于该类型。

创建自定义动画类

创建自己的动画类型可让你更好地控制动画中的对象的方式。 创建自己的动画类型有两种建议方法:可以从 AnimationTimeline 类或 *<类型>*AnimationBase 类派生。 不建议从 *<Type>*Animation 或 *<Type>*AnimationUsingKeyFrames 类派生。

派生自 <Type>AnimationBase

派生自 *<Type>*AnimationBase 类是创建新动画类型的最简单方法。 如果要为已有相应 *<Type>*AnimationBase 类的类型创建新动画,请使用此方法。

实施说明

派生自 *<Type>*Animation 类并实现 GetCurrentValueCore 方法。 GetCurrentValueCore 方法返回动画的当前值。 它采用三个参数:建议的起始值、建议的结束值和 AnimationClock,用于确定动画的进度。

由于 *<类型>*AnimationBase 类继承自 Freezable 类,因此还必须重写 CreateInstanceCore 核心以返回类的新实例。 如果类不使用依赖属性来存储其数据,或者需要在创建后进行额外的初始化,则可能需要重写其他方法;有关详细信息,请参阅 冻结对象概述

有关详细信息,请参阅 *<Type>*AnimationBase 类的 GetCurrentValueCore 方法文档,了解要进行动画处理的类型。 有关示例,请参阅 自定义动画示例

替代方法

如果只想更改动画值的内插方式,请考虑从 *<类型之一派生>*KeyFrame 类。 创建的关键帧可与 WPF 提供的相应 *<类型>*AnimationUsingKeyFrames 一起使用。

派生自 AnimationTimeline

如果想为尚无匹配 WPF 动画的类型创建动画,或者想创建非严格类型化的动画,请从 AnimationTimeline 类派生。

实施说明

派生自 AnimationTimeline 类并重写以下成员:

如果类不使用依赖属性来存储其数据,或者需要在创建后进行额外的初始化,则可能需要重写其他方法;有关详细信息,请参阅 冻结对象概述

建议的模式(用于 WPF 动画)是使用两个继承级别:

  1. 创建从 AnimationTimeline派生的抽象 *<类型>*AnimationBase 类。 此类应该覆盖 TargetPropertyType 方法。 它还应引入新的抽象方法 GetCurrentValueCore 并重写 GetCurrentValue,以便验证默认源值和默认目标值参数的类型,然后调用 GetCurrentValueCore。

  2. 创建另一个从新的 *<Type>*AnimationBase 类继承的类,并替代 CreateInstanceCore 方法、引入的 GetCurrentValueCore 方法和 IsDestinationDefault 属性。

替代方法

如果要为没有相应 From/To/By 动画或关键帧动画的类型制作动画,可以考虑使用 ObjectAnimationUsingKeyFrames。 由于类型很弱,因此 ObjectAnimationUsingKeyFrames 可以对任何类型的值进行动画处理。 此方法的缺点是,ObjectAnimationUsingKeyFrames 仅支持离散内插。

使用 Per-Frame 回调

如果需要完全绕过 WPF 动画系统,请使用此方法。 此方法的一种方案是物理动画,其中在每个动画步骤中,需要根据最后一组对象交互重新计算动画对象的新方向或位置。

实施说明

与本概述中所述的其他方法不同,若要使用每帧回调,无需创建自定义动画或关键帧类。

而是注册包含你想要进行动画处理的对象的容器对象的 Rendering 事件。 每个帧调用一次此事件处理程序方法。 每次 WPF 将可视化树中的持久渲染数据封送到合成树时,都会调用您的事件处理方法。

在事件处理程序中,执行动画效果所需的任何计算,并设置要使用这些值进行动画处理的对象的属性。

若要获取当前帧的呈现时间,可以将与此事件关联的 EventArgs 强制转换为 RenderingEventArgs,该 RenderingTime 属性可用于获取当前帧的呈现时间。

有关详细信息,请参阅 Rendering 页。

另请参阅