相关文章推荐
斯文的茴香  ·  UIElement 類別 ...·  1 年前    · 
从容的柚子  ·  Windows Hello ...·  1 年前    · 
眼睛小的牛腩  ·  使用 MitmProxy ...·  1 年前    · 

.NET 多平台应用 UI (.NET MAUI) Animation 类是所有 .NET MAUI 动画的构建基块,类中的 ViewExtensions 扩展方法创建一个或多个 Animation 对象。

创建 Animation 对象时必须指定多个参数,包括要进行动画处理的属性的开始和结束值,以及更改属性值的回调。 对象 Animation 还可以维护可运行和同步的子动画集合。 有关详细信息,请参阅 子动画

通过调用 Commit 方法,可以运行使用 Animation 类创建的动画,该类可能包含子动画,也可能不包含子动画。 此方法指定动画的持续时间,以及控制是否重复动画的其他项的回调。

Animation 具有一个 IsEnabled 属性,可以检查该属性以确定动画是否已被操作系统禁用,例如在激活节能模式时。

创建 Animation 对象时,通常至少需要三个参数,如以下代码示例所示:

var animation = new Animation(v => image.Scale = v, 1, 2);

在此示例中,实例属性 ScaleImage 动画从值 1 到值 2 定义。 动画值将传递到指定为第一个参数的回调,其中它用于更改属性的值 Scale

动画通过调用 Commit 方法启动:

animation.Commit(this, "SimpleAnimation", 16, 2000, Easing.Linear, (v, c) => image.Scale = 1, () => true);

方法 Commit 不返回 Task 对象。 相反,通知是通过回调方法提供的。

方法中 Commit 指定了以下参数:

  • 第一个参数 (owner) 标识动画的所有者。 这可以是应用动画的视觉元素,也可以是另一个视觉元素,例如页面。
  • 第二个参数 (name) 用名称标识动画。 该名称与所有者组合在一起以唯一标识动画。 然后,可以使用此唯一标识来确定动画是 () AnimationIsRunning 运行,还是 () AbortAnimation 取消动画。
  • 第三个参数 (rate) 指示每次调用构造函数中 Animation 定义的回调方法之间的毫秒数。
  • 第四个参数 (length) 指示动画的持续时间(以毫秒为单位)。
  • 第五个参数 (Easing) 定义要在动画中使用的缓动函数。 或者,缓动函数可以指定为构造函数的参数 Animation 。 有关缓动函数的详细信息,请参阅 缓动函数
  • ) (finished 的第六个参数是将在动画完成时执行的回调。 此回调采用两个参数,第一个参数指示最终值,第二个参数是设置为 booltrue (如果动画已取消)。 或者, finished 可以将回调指定为构造函数的参数 Animation 。 但是,对于单个动画,如果在finished构造函数和 Commit 方法中都Animation指定了回调,则只会执行 方法中指定的Commit回调。
  • ) (repeat 第七个参数是允许重复动画的回调。 它在动画结束时调用,返回 true 指示应重复动画。
  • 在上面的示例中,整体效果是创建一个动画,使用Linear缓动函数将实例的属性Image从 1 增加到 Scale 2,超过 2 秒 (2000 毫秒) 。 每次动画完成时,其 Scale 属性将重置为 1,动画将重复。

    通过为每个动画创建一个 Animation 对象,然后在每个动画上调用 Commit 方法,可以构造相互独立运行的并发动画。

    Animation 还支持子动画,这些动画是 Animation 其他 Animation 对象作为子级添加到的对象。 这样就可以运行和同步一系列动画。 以下代码示例演示如何创建和运行子动画:

    var parentAnimation = new Animation();
    var scaleUpAnimation = new Animation(v => image.Scale = v, 1, 2, Easing.SpringIn);
    var rotateAnimation = new Animation(v => image.Rotation = v, 0, 360);
    var scaleDownAnimation = new Animation(v => image.Scale = v, 2, 1, Easing.SpringOut);
    parentAnimation.Add(0, 0.5, scaleUpAnimation);
    parentAnimation.Add(0, 1, rotateAnimation);
    parentAnimation.Add(0.5, 1, scaleDownAnimation);
    parentAnimation.Commit(this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState(true, false));
    

    或者,可以更简洁地编写代码示例:

    new Animation
        { 0, 0.5, new Animation (v => image.Scale = v, 1, 2) },
        { 0, 1, new Animation (v => image.Rotation = v, 0, 360) },
        { 0.5, 1, new Animation (v => image.Scale = v, 2, 1) }
    }.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true, false));
    

    在这两个示例中,都创建了一个父 Animation 对象,然后向其添加其他 Animation 对象。 方法的前两个参数 Add 指定何时开始和完成子动画。 参数值必须介于 0 和 1 之间,并表示指定子动画将处于活动状态的父动画中的相对周期。 因此,在此示例中, scaleUpAnimation 将在动画的前半部分处于活动状态, scaleDownAnimation 在动画的后半部分将处于活动状态,而 rotateAnimation 将在整个持续时间内处于活动状态。

    此示例的总体效果是动画在 4 秒 (4000 毫秒) 发生。 将 scaleUpAnimation 属性从 Scale 1 到 2(超过 2 秒)进行动画处理。 scaleDownAnimation然后,将Scale属性从 2 到 1(超过 2 秒)进行动画处理。 当这两个缩放动画都发生时,将 rotateAnimation 属性从 0 到 360(超过 4 秒)进行 Rotation 动画处理。 这两个缩放动画还使用缓动函数。 SpringIn缓动函数会导致Image实例在变大之前先收缩,缓SpringOut动函数会导致 Image 在完整动画结束时小于其实际大小。

    使用子动画的对象与不使用子动画的对象之间存在 Animation 许多差异:

  • 使用子动画时,子动画的 finished 回调指示子动画完成的时间, finished 传递给 方法的 Commit 回调指示整个动画完成的时间。
  • 使用子动画时,从 方法的repeatCommit回调返回true不会导致动画重复,但动画将继续运行,且没有新值。
  • 在 方法中包含 Commit 缓动函数,并且缓动函数返回大于 1 的值时,动画将终止。 如果缓动函数返回小于 0 的值,则该值被固定为 0。 若要使用返回小于 0 或大于 1 的值的缓动函数,必须在子动画之一(而不是 方法)中 Commit 指定它。
  • Animation 还包括 WithConcurrent 可用于向父 Animation 对象添加子动画的方法。 但是,它们的 beginfinish 参数值不限制为 0 到 1,但只有对应于 0 到 1 范围的子动画部分将处于活动状态。 例如,如果 WithConcurrent 方法调用定义了一个子动画,该动画面向 Scale 从 1 到 6 的属性,但 beginfinish 值为 -2 和 3, begin 则值 -2 对应于 Scale 值 1, finish 值 3 对应于 Scale 值 6。 由于超出范围 0 和 1 的值在动画中不起任何作用, Scale 因此将仅对属性进行 3 到 6 的动画处理。

    应用可以通过调用 AbortAnimation 扩展方法来取消自定义动画:

    this.AbortAnimation ("SimpleAnimation");
    

    由于动画由动画所有者和动画名称的组合唯一标识,因此必须指定在运行动画时指定的所有者和名称才能取消动画。 因此,此示例将立即取消页面拥有的名为 SimpleAnimation 的动画。

    创建自定义动画

    到目前为止,此处显示的示例演示了使用 类中的方法同样可以实现的 ViewExtensions 动画。 但是, 类的优点 Animation 是它有权访问回调方法,该方法在动画值更改时执行。 这允许回调实现任何所需的动画。 例如,以下代码示例通过将页面的 属性设置为 Color 方法Color.FromHsla创建的值(色调值范围为 0 到 1)来对页面的 属性进行动画BackgroundColor处理:

    new Animation (callback: v => BackgroundColor = Color.FromHsla (v, 1, 0.5),
      start: 0,
      end: 1).Commit (this, "Animation", 16, 4000, Easing.Linear, (v, c) => BackgroundColor = Colors.Black);
    

    生成的动画提供通过彩虹颜色推进页面背景的外观。

    创建自定义动画扩展方法

    类中的 ViewExtensions 扩展方法将属性从其当前值动画转换为指定值。 例如,这会使创建动画方法变得困难, ColorTo 该动画方法可用于对颜色从一个值到另一个值进行动画处理。 这是因为不同的控件具有 类型 Color不同的属性。 VisualElement虽然 类定义BackgroundColor属性,但这并不总是要进行动画处理所需的Color属性。

    此问题的解决方案是不让 ColorTo 方法面向特定 Color 属性。 相反,可以使用回调方法编写,该方法将内 Color 插值传递回调用方。 此外, 方法将采用 start 和 end Color 参数。

    方法 ColorTo 可以作为扩展方法实现,该扩展方法使用 Animate 类中的 AnimationExtensions 方法来提供其功能。 这是因为 方法 Animate 可用于目标不是 类型 double的属性,如以下代码示例所示:

    public static class ViewExtensions
        public static Task<bool> ColorTo(this VisualElement self, Color fromColor, Color toColor, Action<Color> callback, uint length = 250, Easing easing = null)
            Func<double, Color> transform = (t) =>
                Color.FromRgba(fromColor.Red + t * (toColor.Red - fromColor.Red),
                               fromColor.Green + t * (toColor.Green - fromColor.Green),
                               fromColor.Blue + t * (toColor.Blue - fromColor.Blue),
                               fromColor.Alpha + t * (toColor.Alpha - fromColor.Alpha));
            return ColorAnimation(self, "ColorTo", transform, callback, length, easing);
        public static void CancelAnimation(this VisualElement self)
            self.AbortAnimation("ColorTo");
        static Task<bool> ColorAnimation(VisualElement element, string name, Func<double, Color> transform, Action<Color> callback, uint length, Easing easing)
            easing = easing ?? Easing.Linear;
            var taskCompletionSource = new TaskCompletionSource<bool>();
            element.Animate<Color>(name, transform, callback, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));
            return taskCompletionSource.Task;
    

    方法 Animate 需要一个 transform 参数,即回调方法。 此回调的输入始终 double 为 0 到 1。 因此,在此示例中, ColorTo 方法定义其自己的转换,该转换 Func 接受 double 范围从 0 到 1,并返回与 Color 该值对应的值。 该值Color是通过内插提供的Color两个参数的 RedGreenBlueAlpha 值来计算的。 然后,该值 Color 将传递给回调方法,以应用于属性。 此方法允许 ColorTo 方法对任何指定的 Color 属性进行动画处理:

    await Task.WhenAll(
      label.ColorTo(Colors.Red, Colors.Blue, c => label.TextColor = c, 5000),
      label.ColorTo(Colors.Blue, Colors.Red, c => label.BackgroundColor = c, 5000));
    await this.ColorTo(Color.FromRgb(0, 0, 0), Color.FromRgb(255, 255, 255), c => BackgroundColor = c, 5000);
    await boxView.ColorTo(Colors.Blue, Colors.Red, c => boxView.Color = c, 4000);
    

    在此代码示例中ColorTo, 方法对 的 和 BackgroundColor 属性Label进行动画处理TextColorBackgroundColor 页面的 属性和 ColorBoxView属性。