弹簧动画
介绍了如何使用弹簧 NaturalMotionAnimation。
先决条件
我们在此假设你熟悉这些文章中所述的概念:
为什么要使用弹簧?
弹簧是一种常见的运动体验,我们都曾经在生活中经历过;从螺旋弹簧玩具到包含弹簧块的物理教室体验。 弹簧的运动不稳定,这通常会为观察它的人带来有趣而令人愉快的情绪反应。 因此,对于那些渴求创建更为生动的运动体验的人来说,弹簧的运动在应用程序 UI 中得到了很好的运用:“弹出”给最终用户,而不是传统的贝塞尔曲线。 在这些情况下,弹簧运动不仅能产生更为生动的运动体验,还能帮助吸引人们注意新的或目前正在显示动画效果的内容。 根据应用程序的品牌或运动语言,弹簧的振荡更加显然而显眼,但在其他情况下,它的运动不明显。
在 UI 中使用弹簧
如上文所述,弹簧可能是很有用的运动,可集成到应用中,给人们带来非常熟悉又有趣的 UI 体验。 UI 中的常见弹簧用法:
弹簧用法描述 | 视觉示例 |
---|---|
让运动体验“弹出”并且看起来更生动 (伸缩动画处理) | |
使运动体验感觉起来更加有力(将 Offset 进行动画处理) |
在以上每一种情况下,弹簧运动都可通过“弹到”和围绕一个新值振荡或围绕当前值振荡(有初始速度)来触发。
定义弹簧运动
可以通过使用 NaturalMotionAnimation API 创建弹簧体验。 具体来说,基于 Compositor 使用 Create* 方法创建 SpringNaturalMotionAnimation。 然后,可以定义以下运动属性:
- DampingRatio – 表示在动画中使用的弹簧运动的阻尼级别。
阻尼比值 | 说明 |
---|---|
DampingRatio = 0 | Undamped – 弹簧将振荡很长时间 |
0 < DampingRatio < 1 | Underdamped – 弹簧将由少变多增加振荡幅度。 |
DampingRatio = 1 | Criticallydamped – 弹簧不会振荡。 |
DampingRation > 1 | Overdamped – 弹簧将通过突然减速而不振荡快速到达其目的地 |
- Period – 弹簧执行单次振荡所花费的时间。
- Final/Starting Value – 定义弹簧运动的开始和结束位置(如果未定义,则开始值和/或最终值将是当前值)。
- 初始速度 – 运动的编程初始速度。
你还可以定义一组与 KeyFrameAnimation 相同的运动属性:
- DelayTime/DelayBehavior
- StopBehavior
在对 Offset 和 Scale/Size 进行动画处理的常见情况下,Windows 设计团队为不同类型的弹簧的 DampingRatio 和 Period 推荐了以下值:
属性 | 普通弹簧 | 带阻尼弹簧 | 较小阻尼弹簧 |
---|---|---|---|
Offset | Damping Ratio = 0.8 Period = 50 ms |
Damping Ratio = 0.85 Period = 50 ms |
Damping Ratio = 0.65 Period = 60 ms |
Scale/Size | Damping Ratio = 0.7 Period = 50 ms |
Damping Ratio = 0.8 Period = 50 ms |
Damping Ratio = 0.6 Period = 60 ms |
定义好这些属性之后,可以将弹簧 NaturalMotionAnimation 传入 CompositionObject 的 StartAnimation 或 InteractionTracker InertiaModifier 的 Motion 属性。
示例
在本示例中,创建导航和画布 UI 体验:当用户单击展开按钮时,导航窗格以震荡运动方式用动画效果弹出。
首先,在导航窗格显示时在所单击事件内定义弹簧动画。 然后使用 InitialValueExpression 功能通过 Expression 定义 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);
}
现在,如果需要将此运动与输入关联,该怎么办? 如果最终用户轻扫,窗格是否以弹簧运动出现? 更重要的是,如果用户更用力或更快地轻扫,运动能够根据最终用户的速度相应调节适应。
为此,可以使用此处的弹簧动画,将它传入 InteractionTracker 的 InertiaModifier。 有关 InputAnimation 和 InteractionTracker 的详细信息,请参阅 InteractionTracker 的自定义操作体验。 假定本示例代码已经设置了 InteractionTracker 和 VisualInteractionSource。 我们主要介绍如何创建用在 NaturalMotionAnimation 中的 InertiaModifier,本例中是弹簧。
// InteractionTracker and the VisualInteractionSource previously setup
// The open and close ScalarSpringAnimations defined earlier
private void SetupInput()
{
// Define the InertiaModifier to manage the open motion
var openMotionModifer = InteractionTrackerInertiaNaturalMotion.Create(compositor);
// Condition defines to use open animation if panes in non-expanded view
// Property set value to track if open or closed is managed in other part of code
openMotionModifer.Condition = _compositor.CreateExpressionAnimation(
"propset.expanded == false");
openMotionModifer.Condition.SetReferenceParameter("propSet", _propSet);
openMotionModifer.NaturalMotion = _openSpringAnimation;
// Define the InertiaModifer to manage the close motion
var closeMotionModifier = InteractionTrackerInertiaNaturalMotion.Create(_compositor);
// Condition defines to use close animation if panes in expanded view
// Property set value to track if open or closed is managed in other part of code
closeMotionModifier.Condition =
_compositor.CreateExpressionAnimation("propSet.expanded == true");
closeMotionModifier.Condition.SetReferenceParameter("propSet", _propSet);
closeMotionModifier.NaturalMotion = _closeSpringAnimation;
_tracker.ConfigurePositionXInertiaModifiers(new
InteractionTrackerInertiaNaturalMotion[] { openMotionModifer, closeMotionModifier});
// Take output of InteractionTracker and assign to the pane
var exp = _compositor.CreateExpressionAnimation("-tracker.Position.X");
exp.SetReferenceParameter("tracker", _tracker);
ElementCompositionPreview.GetElementVisual(pageNavigation).
StartAnimation("Translation.X", exp);
}
现在可以在 UI 中实现编程弹簧动画和输入驱动的弹簧动画!
总之,在应用中使用弹簧动画的步骤是:
- 基于 Compositor 创建 SpringAnimation。
- 如果需要非默认值,请定义 SpringAnimation 的属性:
- DampingRatio
- 周期
- Final Value
- 初始值
- Initial Velocity
- 分配给目标。
- 如果要对 CompositionObject 属性进行动画处理,请将 SpringAnimation 作为参数传入 StartAnimation。
- 如果需要使用输入,则将 InertiaModifier 的 NaturalMotion 属性设置为 SpringAnimation。
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈