动画和计时系统概述
本主题描述计时系统如何使用动画、Timeline 和 Clock 类来对属性进行动画处理。
先决条件
为了了解本主题,应该能够使用 WPF 动画来对属性进行动画处理,如动画概述中所述。 本主题还有助于熟悉依赖属性;有关详细信息,请参阅依赖属性概述。
时间线和时钟
动画概述介绍了 Timeline 如何表示一个时间段,还介绍了动画是生成输出值的某种类型的 Timeline。 Timeline 本身除了仅仅描述一个时间段外,不执行任何其他操作。 实际工作由时间线的 Clock 对象执行。 同样,动画实际上不对属性进行动画处理:动画类描述应如何计算输出值,但为动画创建的是 Clock,该动画会促进动画输出并将其应用于属性。
Clock 是一种特殊类型的对象,它维护 Timeline 的与计时相关的运行时状态。 它提供了对动画和计时系统至关重要的三位信息:CurrentTime、CurrentProgress 和 CurrentState。 Clock 使用由它的 Timeline 所描述的计时行为(Duration、RepeatBehavior、AutoReverse 等)来确定它的当前时间、进度和状态。
在大多数情况下,会自动为时间线创建 Clock。 使用 Storyboard 或 BeginAnimation 方法进行动画处理时,系统会自动为时间线和动画创建时钟,并将这些时钟应用于其目标属性。 还可以使用 Clock 的 CreateClock 方法显式创建 Timeline。 MediaTimeline.CreateClock 方法为调用它的 Timeline 创建适当类型的时钟。 如果 Timeline 包含子时间线,还会为这些子时间线创建 Clock 对象。 生成的 Clock 对象将以树的形式排列,这些树与这些对象创建时所在的 Timeline 对象树的结构相匹配。
不同类型的时间线具有不同类型的时钟。 下表显示的是与一些其他 Clock 类型相对应的 Timeline 类型。
时间线类型 | 时钟类型 | 时钟用途 |
---|---|---|
动画(继承自 AnimationTimeline) | AnimationClock | 为依赖属性生成输出值。 |
MediaTimeline | MediaClock | 处理媒体文件。 |
ParallelTimeline | ClockGroup | 对子 Clock 对象进行分组和控制 |
Storyboard | ClockGroup | 对子 Clock 对象进行分组和控制 |
可以使用 AnimationClock 方法,将创建的任何 ApplyAnimationClock 对象应用于兼容的依赖属性。
在严重依赖性能的方案(如对大量类似的对象进行动画处理)中,管理对自有 Clock 的使用可能会提高性能。
时钟和时间管理器
对 WPF 中的对象进行动画处理时,时间管理器会管理系统为时间线创建的 Clock 对象。 时间管理器是 Clock 对象树的根,并控制该树中的时间流。 时间管理器是为每个 WPF 应用程序自动创建的,对应用程序开发人员不可见。 时间管理器每秒钟“滴答”多次;每秒发生的实际滴答次数取决于可用的系统资源。 在每个滴答过程中,时间管理器都会计算计时树中所有 ActiveClock 对象的状态。
下图演示时间管理器、AnimationClock 和经过动画处理的依赖属性之间的关系。
对属性进行动画处理
当时间管理器滴答时,它会更新应用程序中每个 ActiveClock 的时间。 如果 Clock 是 AnimationClock,它会使用从中创建它的 GetCurrentValue 的 AnimationTimeline 方法来计算其当前的输出值。 AnimationClock 向 AnimationTimeline 提供当前的本地时间、一个输入值(通常是属性的基值)和一个默认目标值。 使用 GetValue 方法或其 CLR 访问器依据属性检索动画对象的值时,将获取 AnimationClock 的输出。
时钟组
上一部分描述了不同类型的时间线具有不同类型的 Clock 对象。 下图演示时间管理器、ClockGroup、AnimationClock 和经过动画处理的依赖属性之间的关系。 创建 ClockGroup 以用于对其他时间线进行分组的时间线,如 Storyboard 类,该类对动画和其他时间线进行分组。
ClockGroup
组合
可以将多个时钟与一个属性相关联,在这种情况下,每个时钟都将上一个时钟的输出值用作其基值。 下图演示了应用于同一个属性的三个 AnimationClock 对象。 时钟1 将经过动画处理的属性的基值用作其输入,并使用该值生成输出。 时钟2 将时钟1 的输出用作其输入,并使用该值生成输出。 时钟3 将时钟2 的输出用作其输入,并使用该值生成输出。 如果多个时钟同时影响同一个属性,则认为这些时钟位于一个组合链中。
组合链
请注意,尽管在组合链中 AnimationClock 对象的输入和输出之间创建了关系,但是这些对象的计时行为不会受到影响;Clock 对象(包括 AnimationClock 对象)对于它们的父级 Clock 对象具有分层依赖性。
若要对同一个属性应用多个时钟,在应用 Compose、动画或 HandoffBehavior 时,请使用 StoryboardAnimationClock。
滴答和事件合并
除了计算输出值以外,时间管理器还会在它每滴答一次时执行其他工作:它会确定每个时钟的状态并根据需要引发事件。
尽管滴答频率很高,但是在两次滴答之间还是有可能会发生许多事情。 例如,Clock 可能会停止、启动、再次停止,在这种情况下其 CurrentState 值将更改三次。 从理论上讲,在一个时钟周期内,可以多次引发 CurrentStateInvalidated 事件;但是,计时引擎会合并事件,因此 CurrentStateInvalidated 事件在每个时钟周期内只能引发一次。 这同样适用于所有的计时事件:对于给定的 Clock 对象,针对每种类型最多引发一个事件。
当 Clock 在两次滴答之间切换状态并恢复到最初的状态(如从 Active 更改为 Stopped,然后再恢复为 Active)时,仍将发生相关的事件。
有关计时事件的详细信息,请参阅计时事件概述。
属性的当前值和基值
可进行动画处理的属性具有两个值:基值和当前值。 使用属性的 CLR 访问器或 SetValue 方法设置属性时,可以设置属性的基值。 对于尚未进行动画处理的属性,基值和当前值相同。
对属性进行动画处理时,AnimationClock 会设置属性的当前值。 当 GetValue 为 AnimationClock 或 AnimationClock 时,通过属性的 CLR 访问器或 Active 方法检索属性的值将返回 Filling 的输出。 可以使用 GetAnimationBaseValue 方法检索属性的基值。