一、自定义动画概述:用来扩展 WPF 动画系统:创建自定义关键帧、动画类或者使用逐帧回调来绕过该系统。

clipboard.png

1. 以上为扩展动画系统总览

2. 创建一个自定义关键帧

  1. 最简单的方法。 当您需要对关键帧动画使用一种不同的内插方法时,可使用此方法。每个关键帧对象都执行三个功能:

    1. 使用它的 Value 属性指定一个目标值。
    2. 使用它的 KeyTime 属性指定达到该值的时间。
    3. 通过实现 InterpolateValueCore 方法在前一关键帧的值与它自己的值之间进行内插。
  2. 派生自 <类型>KeyFrame 抽象类并实现 InterpolateValueCore 方法。 InterpolateValueCore 方法返回关键帧的当前值。 它采用两个参数:前一个关键帧的值以及 0 到 1 之间的进度值。 进度值为 0 表示关键帧刚刚启动,值为 1 表示关键帧刚刚完成,应返回它的 Value 属性所指定的值。
  3. 因为 <类型>KeyFrame 类继承自 Freezable 类,所以您还必须重写 CreateInstanceCore 核心来返回类的新实例。 如果该类未使用依赖项属性来存储其数据或者在创建后需要额外的初始化,则您可能还需要重写其他方法;
  4. 在创建自定义 <类型>KeyFrame 动画后,您可以将它与该类型的 <类型>AnimationUsingKeyFrames 一起使用。

3. 创建自定义动画类:

  1. 更好地控制对象的动画方式。 有两种推荐方法来创建自己的动画类型:从 AnimationTimeline 类或 <类型>AnimationBase 类派生。 不推荐从 <类型>Animation 或 <类型>AnimationUsingKeyFrames 类派生。
  2. 从 <类型>AnimationBase 类派生是创建新的动画类型的最简单方法。:它派生出 <类型>Animation 类并实现 GetCurrentValueCore 方法。 GetCurrentValueCore 方法返回动画的当前值。 它采用三个参数:建议的起始值、建议的结束值以及您用于确定动画进度的 AnimationClock。还必须重写 CreateInstanceCore 核心来返回类的新实例。
  3. 当您希望为还没有匹配的 WPF 动画的类型创建动画,或者希望创建非强类型的动画时,应当从 AnimationTimeline 类派生并重写以下成员:

    1. CreateInstanceCore – 如果新类是具体的,则必须重写 CreateInstanceCore 来返回类的新实例。
    2. GetCurrentValue – 重写此方法以返回动画的当前值。 它采用三个参数:默认初始值、默认目标值以及 AnimationClock。 使用 AnimationClock 来获取动画的当前时间或进度。 您可以选择是否使用默认初始值和目标值。
    3. IsDestinationDefault – 重写此属性来指示您的动画是否使用 GetCurrentValue 方法指定的默认目标值。
    4. TargetPropertyType – 重写此属性来指示动画产生的输出的 Type。
  4. 推荐的范例(由 WPF 动画使用)将使用两个继承级别:

    1. 创建一个派生自 AnimationTimeline 的抽象 <类型>AnimationBase 类。 此类应重写 TargetPropertyType 方法。 它还应引入一个新的抽象方法 GetCurrentValueCore 并重写 GetCurrentValue,以便验证默认初始值和默认目标值参数的类型,然后调用 GetCurrentValueCore。
    2. 创建另一个类,该类继承自新的 <类型>AnimationBase 类并重写 CreateInstanceCore 方法、您引入的 GetCurrentValueCore 方法以及 IsDestinationDefault 属性。
  5. 其他方法:

    1. 如果您希望对没有对应 From/To/By 动画或关键帧动画的类型进行动画处理,应考虑使用 ObjectAnimationUsingKeyFrames。 因为它是弱类型,所以 ObjectAnimationUsingKeyFrames 可以对任何类型的值进行动画处理。 此方法的弱点是 ObjectAnimationUsingKeyFrames 仅支持离散内插。

4. 使用逐帧回调:当需要完全绕过 WPF 动画系统时使用此方法。

  1. 与本概述中介绍的其他方法不同,要使用逐帧回调,不需要创建自定义动画或关键帧类,此方法的一种方案是物理动画,在其中的每一个动画步骤,都需要基于最后一组对象交互重新计算动画对象的新方向或位置。
  2. 而应注册需要进行动画处理的对象所在对象的Rendering 事件。 每帧调用一次此事件处理程序方法。 每次 WPF 将可视化树中持久呈现的数据封送到组合树中时,都将调用事件处理程序方法。
  3. 在事件处理程序中,执行动画效果所需的任意计算,并设置需要使用这些值进行动画处理的对象的属性。
  4. 要获取当前帧的显示时间,可以将与此事件关联的 EventArgs 强制转换为 RenderingEventArgs,从而提供可用于获取当前帧呈现时间的 RenderingTime 属性。
  5. 参考:CompositionTarget.Rendering 事件

二、Freezable 对象概述:使用和创建 Freezable 对象,这些对象提供可帮助改进应用程序性能的特殊功能。Freezable 对象的示例包括画笔、钢笔、变换、几何图形和动画。
1. 介绍:

  1. Freezable 是一种特殊的对象类型,具有两个状态:解冻和冻结。 当处于解冻状态时, Freezable 的行为与任何其他对象的行为一样。 Freezable 一旦冻结,便无法修改。
  2. Freezable 提供了一个 Changed 事件以将对对象所做的任何修改通知给观察程序。 冻结 Freezable 可以改进其性能,因为它不再需要因更改通知而消耗资源。 冻结的 Freezable 也可以在线程之间共享,而解冻的 Freezable 则不能。
  3. 虽然 Freezable 类具有许多应用程序,但WPF中的大多数 Freezable 对象都与图形子系统相关。
  4. Freezable 类使您在使用某些图形系统对象时更加轻松,并且有助于改进应用程序性能。 从 Freezable 继承的类型示例包括 Brush、 Transform 和 Geometry 类。 由于它们包含非托管资源,因此系统必须监视这些对象的修改情况,然后在对原始对象进行了更改的情况下,更新其相应的非托管资源。 即使实际上您并没有修改图形系统对象,但系统也必须花费一些资源来监视该对象,以防您对其进行更改。

2. 使用Freezable:

  1. 若要使 Freezable 不可修改,可调用其 Freeze 方法。 冻结某个包含 Freezable 对象的对象时,也会冻结这些被包含的对象。 例如,如果您冻结某个PathGeometry,则它包含的图形和线段也会被冻结。
  2. 为了避免引发此异常,可以使用 IsFrozen 方法来确定 Freezable 是否处于冻结状态。
  3. 注意: 由于无法对冻结的 Freezable 进行动画处理,因此当您尝试使用 Storyboard 对冻结的 Freezable 对象进行动画处理时,动画系统将自动创建这些对象的可修改复本。 为了消除由于克隆而导致的性能系统开销,当您希望对某个对象进行动画处理时,请使其保持解冻状态。
  4. 若要冻结在标记中声明的 Freezable 对象,请使用 PresentationOptions:Freeze 特性,一般声明为页资源,并冻结它。
  5. 若要使用 Freeze 特性,必须映射到表示选项命名空间:xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
  6. 由于并非所有 XAML 读取器都能识别该特性,因此建议使用 mc:Ignorable 特性:xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="PresentationOptions"
  7. Freezable 一旦冻结,便不能再修改或解冻;不过,您可以使用 Clone 或 CloneCurrentValue 方法创建一个解冻的复本。注意:无论使用哪一种克隆方法,都不会将动画复制到新的 Freezable。
  8. Clone 和 CloneCurrentValue 方法可生成 Freezable 的深层副本。 如果该 Freezable 包含其他冻结的 Freezable 对象,则还会克隆这些对象使它们可以修改。 例如,如果克隆某个冻结的 PathGeometry 以便使其可以修改,则还会复制它包含的图形和线段,使它们可以修改。

3. 创建自己的 Freezable 类:

  1. 从 Freezable 派生的类可以获取以下功能:

    1. 特殊的状态:只读(冻结)状态和可写状态。
    2. 线程安全:冻结的 Freezable 可以在线程之间共享。
    3. 详细的更改通知:与其他 DependencyObject 不同,Freezable 对象会在子属性值更改时提供更改通知。
    4. 轻松克隆:Freezable 类已经实现了多种生成深层复本的方法。
  2. Freezable 是一种 DependencyObject,因而使用依赖项属性系统。 您的类属性不必是依赖项属性,但使用依赖项属性可以减少必须编写的代码量,因为设计 Freezable 类时考虑了依赖项属性。
  3. 每个 Freezable 子类都必须重写 CreateInstanceCore 方法。 如果您的类对于其所有数据都使用依赖项属性,则您的工作已完成。
  4. 如果您的类包含非依赖项属性数据成员,则还必须重写以下方法:CloneCore、CloneCurrentValueCore、GetAsFrozenCore、GetCurrentValueAsFrozenCore、FreezeCore及读写规则等。注意:通过调用基实现来开始重写的每个 Freezable 方法,这一点非常重要。

三、 缓动函数EasingFunctionBase:

clipboard.png

  1. 缓动模式EasingMode为三种,如下效果图:

clipboard.png

clipboard.png

clipboard.png

clipboard.png

clipboard.png

BounceEase 类:表示创建动画反弹效果的缓动函数。

  1. 使用 Bounces 属性指定弹跳次数并使用 Bounciness 属性指定弹跳程度(弹跳的弹性大小)。 Bounciness 属性指定下一个反弹的幅度缩放。
  2. 例如,反弹度值 2 会使渐入中下一个反弹的幅度翻倍,并且会使渐出中下一个反弹的幅度减半。

ElasticEase 类:该函数创建一个模仿弹簧来回振荡直到静止的动画。

  1. 可以使用 Oscillations 属性指定动画来回振动的次数,以及使用 Springiness 属性指定振动弹性的张紧程度。
  2. 因为此动画导致值来回振动,所以此动画可能会意外插入到负数中。 当对不支持负数的属性进行动画处理时,这可能会引发错误。 例如,如果您将此动画应用到对象的 Height(例如 对于, EaseIn 的 EasingMode,为从 0 到 200),则该动画将尝试对 Height 插入负数,从而引发错误。

CircleEase 类:该函数使用圆函数创建加速和/或减速的动画。

  1. 样例中为真正自定义,与此缓动函数完全不一样

BackEase 类:该函数在开始沿指示的路径进行动画处理之前轻微收缩动画的动作。

  1. Amplitude 获取或设置与 BackEase 动画关联的收缩的幅度。

四、DoubleAnimationBase.GetCurrentValueCore 方法 :计算一个值,该值表示正在进行动画处理的属性的当前值,并由宿主动画确定。为抽象protected方法,由子类继承实现

  1. AnimationTimeline.GetCurrentValue:获取动画的当前值,为虚方法,子类DoubleAnimationBase继承实现此方法,为GetCurrentValue(Object, Object, AnimationClock);然后又重载一个GetCurrentValue(Double, Double,AnimationClock)。此外给出一个抽象的GetCurrentValueCore(Double, Double,AnimationClock),由子类实现计算当前值。此处好一阵才弄明白由来,为什么子类必须要重写。
  2. protected abstract double GetCurrentValueCore(double defaultOriginValue,
    double defaultDestinationValue,AnimationClock animationClock)

    1. defaultOriginValue :建议使用的原始值。如果动画没有自己显式设置的起始值,则使用该值。
    2. defaultDestinationValue :建议使用的目标值。如果动画没有自己显式设置的结束值,则使用该值。
    3. animationClock :用于生成宿主动画使用的 CurrentTime 或 CurrentProgress 的 AnimationClock。
    4. 返回值Double:计算得出的属性值,该值由当前动画确定。

李志玮
22 声望34 粉丝

求索~~


引用和评论

0 条评论