通过


弹簧动画

本文介绍如何在 WinUI 中使用 Spring NaturalMotionAnimations。

先决条件

此处,我们假设你熟悉以下文章中讨论的概念:

为什么是泉水?

弹簧是一种我们在生活中的某个阶段都经历过的常见动态体验;从弹簧玩具到物理教室里与系在弹簧上的木块的体验。 弹簧的振荡运动经常引起观察者轻松和愉快的情感反应。 因此,弹簧的运动能很好地转化为应用程序 UI,对于那些希望创建更生动、更吸引最终用户的运动体验的人来说,比传统的立方贝塞尔更有表现力。 在这些情况下,春季运动不仅创造了一个活泼的运动体验,而且还有助于引起人们对新内容或当前动画内容的注意。 根据应用程序品牌或运动语言,振荡更为明显,但在其他情况下,振荡更为微妙。

运动的弹簧动画 运动与立方贝塞尔动画

在 UI 中使用弹簧

如前所述,斯普林斯是一个有用的动作,可以集成到 WinUI 应用,以引入非常熟悉和俏皮的 UI 体验。 UI 中弹簧的常见用法包括:

Spring 使用说明 视觉示例
使动态体验更加“突出”,看起来更生动。 (动画缩放) 使用弹性动画缩放动画
使运动体验微妙地感觉更精力充沛 (动画偏移) 利用弹簧动画调整位移运动

在这些情况下,可以通过“弹跳到”并在新值附近振荡或在当前值周围以初始速度振荡来触发弹簧运动。

春季动画振荡

定义弹簧运动

使用 NaturalMotionAnimation API 创建春季体验。 具体而言,使用 Compositor 中的 Create* 方法创建 SpringNaturalMotionAnimation。 然后,可以定义运动的以下属性:

  • DampingRatio - 表示动画中弹簧运动的阻尼程度。
抑制比率值 说明
阻尼比(DampingRatio)= 0 无阻尼 - 弹簧将振荡很长时间
0 < 阻尼比 < 1 欠阻尼的弹簧会从小幅到大幅振荡。
阻尼比 = 1 严重阻尼 - 弹簧不会产生振荡。
DampingRatio > 1 过阻尼——弹簧会快速到达目的地,并且由于突然减速而没有振荡。
  • 周期 – 弹簧完成一次振荡所需的时间。
  • Final / Starting Value – 定义弹簧运动的起始值和结束值(如果未定义,起始值和/或最终值将取当前值)。
  • 初始速度 - 用编程设置运动的初始速度。

还可以定义与 KeyFrameAnimations 相同的动作的一组属性:

  • DelayTime/Delay 行为
  • StopBehavior

在常见的对偏移量和缩放/尺寸进行动画处理的情况下,Windows 设计团队针对不同类型的弹簧推荐以下阻尼比和周期值:

财产 普通春季 减震弹簧 低阻尼弹簧
Offset 阻尼比率 = 0.8
周期 = 50 毫秒
阻尼比率 = 0.85
周期 = 50 毫秒
阻尼比率 = 0.65
周期 = 60 毫秒
比例/尺寸 阻尼比率 = 0.7
周期 = 50 毫秒
阻尼比率 = 0.8
周期 = 50 毫秒
阻尼比率 = 0.6
周期 = 60 毫秒

定义属性后,即可将 spring 类型的 NaturalMotionAnimation 传递给 CompositionObject 的 StartAnimation 方法,或传递给 InteractionTracker InertiaModifier 的 Motion 属性。

示例

在此示例中,你将创建一个导航和画布的用户界面体验,当用户单击展开按钮时,导航窗格会以弹性振动的动画效果展开。

单击时的 Spring 动画

首先,在单击事件中定义当导航窗格出现时的 spring 动画。 然后,使用 InitialValueExpression 功能定义动画的属性,以使用表达式定义 FinalValue。 还可以跟踪窗格是否已打开,并在准备就绪时启动动画。

private void Button_Clicked(object sender, RoutedEventArgs e)
{
    _springAnimation = _compositor.CreateSpringScalarAnimation();
    _springAnimation.DampingRatio = 0.75f;
    _springAnimation.Period = TimeSpan.FromSeconds(0.5);

    if (!_expanded)
    {
        _expanded = true;
        _propSet.InsertBoolean("expanded", true);
        _springAnimation.InitialValueExpression["FinalValue"] = "this.StartingValue + 250";
    }
    else
    {
        _expanded = false;
        _propSet.InsertBoolean("expanded", false);
        _springAnimation.InitialValueExpression["FinalValue"] = "this.StartingValue - 250";
    }

    _naviPane.StartAnimation("Offset.X", _springAnimation);
}

现在,如果想要将此动作绑定到输入,该怎么办? 那么,如果终端用户轻扫,窗格会呈现出弹簧运动? 更重要的是,如果用户用力或快速地滑动,系统会根据用户的滑动速度进行调整。

轻扫时的 Spring 动画

为此,您可以采用我们的同一 Spring Animation,并将其传递给 InteractionTracker 中的惯性修饰符。 有关 InputAnimations 和 InteractionTracker 的详细信息,请参阅 InteractionTracker 的自定义操作体验。 我们假设在本代码示例中,你已设置 InteractionTracker 和 VisualInteractionSource。 我们将重点创建 InertiaModifiers,它将采用 NaturalMotionAnimation,在本例中为弹簧。

// InteractionTracker and the VisualInteractionSource were previously set up.
// The open and close ScalarSpringAnimations were defined earlier.
private void SetupInput()
{
    // Define the InertiaModifier to manage the open motion.
    var openMotionModifier = InteractionTrackerInertiaNaturalMotion.Create(_compositor);

    // Use the open animation if the pane is not expanded.
    openMotionModifier.Condition = _compositor.CreateExpressionAnimation(
        "propSet.expanded == false");
    openMotionModifier.Condition.SetReferenceParameter("propSet", _propSet);
    openMotionModifier.NaturalMotion = _openSpringAnimation;

    // Define the InertiaModifier to manage the close motion.
    var closeMotionModifier = InteractionTrackerInertiaNaturalMotion.Create(_compositor);

    // Use the close animation if the pane is expanded.
    closeMotionModifier.Condition = _compositor.CreateExpressionAnimation(
        "propSet.expanded == true");
    closeMotionModifier.Condition.SetReferenceParameter("propSet", _propSet);
    closeMotionModifier.NaturalMotion = _closeSpringAnimation;

    _tracker.ConfigurePositionXInertiaModifiers(new InteractionTrackerInertiaNaturalMotion[]
    {
        openMotionModifier,
        closeMotionModifier,
    });

    // Take the InteractionTracker output and assign it to the pane.
    var exp = _compositor.CreateExpressionAnimation("-tracker.Position.X");
    exp.SetReferenceParameter("tracker", _tracker);

    ElementCompositionPreview.GetElementVisual(pageNavigation)
        .StartAnimation("Translation.X", exp);
}

现在,你的 UI 中同时具有程序化和输入驱动的弹簧动画!

总之,在应用中使用春季动画的步骤:

  1. 在 Compositor 上创建 SpringAnimation。
  2. 如果需要非默认值,请定义 SpringAnimation 的属性:
    • 阻尼比
    • 期限
    • 最终值
    • 初始值
    • 初速度
  3. 分配给对象。
    • 如果要对 CompositionObject 属性进行动画处理,请将 SpringAnimation 作为参数传递给 StartAnimation。
    • 如果要与输入一起使用,请将 InertiaModifier 的 NaturalMotion 属性设置为 SpringAnimation。