自定义动画概述

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

先决条件

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

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

扩展动画系统

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

  • 通过继承自 *Type>*<KeyFrame 类之一(例如DoubleKeyFrame)创建自定义关键帧对象。 此方法使用 WPF 动画引擎的大部分内置功能。

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

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

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

需要执行以下操作时: 使用此方法
自定义在具有相应 *<Type>*AnimationUsingKeyFrames 的类型中的值之间的插值 创建自定义关键帧。 有关详细信息,请参阅“ 创建自定义关键帧 ”部分。
自定义的不仅仅是具有相应 *<Type>*Animation 的类型值之间的内插。 创建自定义动画类,该类继承自 *<Type>*AnimationBase 类,该类对应于要进行动画处理的类型。 有关详细信息,请参阅“ 创建自定义动画类 ”部分。
对没有相应 WPF 动画的类型进行动画处理 使用ObjectAnimationUsingKeyFrames或创建一个继承自AnimationTimeline的类。 有关详细信息,请参阅“ 创建自定义动画类 ”部分。
为多个对象制作动画,这些动画的值是每帧计算的,并基于最后一组对象交互。 使用每帧回调。 有关详细信息,请参阅“创建使用 Per-Frame 回调”部分。

创建自定义关键帧

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

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

  • 指定应使用其 KeyTime 属性访问该值的时间。

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

实现说明

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

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

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

创建自定义动画类

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

派生自 <Type>AnimationBase

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

实现说明

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

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

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

替代方法

如果你只是想更改动画值的插值方式,可以考虑继承自 *<Type>*KeyFrame 类之一。 创建的关键帧可以与 WPF 提供的相应 *<Type>*AnimationUsingKeyFrame 一起使用。

派生自 AnimationTimeline

当您想为尚未具有匹配 WPF 动画的类型创建动画,或者想创建非强类型化的动画时,请从 AnimationTimeline 类派生。

实现说明

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

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

推荐的范式(用于 WPF 动画)是使用两层继承级别:

  1. 创建一个抽象的*<Type>*AnimationBase 类,该类派生自AnimationTimeline。 此类应重写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 页面。

另请参阅