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