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