使用惯性修饰符创建吸附点

在本文中,我们将更深入地探讨如何使用 InteractionTracker 的惯性修改器功能来创建贴靠到指定点的运动体验。

先决条件

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

什么是捕捉点,为什么它们很有用?

在创建自定义操作体验时, 有时在可滚动/可缩放画布中创建专用的 位置点 会很有帮助,以便 InteractionTracker 最终能够停留于这些点上。 这些通常称为 吸附点

请注意,在以下示例中,滚动如何使 UI 处于不同图像之间的尴尬位置:

无对齐点的滚动

如果添加贴靠点,那么在停止滚动图像时,它们会快速贴合到指定的位置。 借助贴靠点,它使滚动浏览图像的体验更加清晰且响应更快速。

使用单个对齐点滚动

InteractionTracker 和 InertiaModifiers

在使用 InteractionTracker 构建自定义操作体验时,可以通过利用惯性修饰器来创建贴靠点运动体验。 InertiaModifiers 本质上是一种方法,用于定义 InteractionTracker 在进入惯性状态时到达其目的地的位置或方式。 可以应用 InertiaModifiers 来影响 InteractionTracker 的 X 或 Y 位置或缩放属性。

有 3 种类型的 InertiaModifiers:

  • InteractionTrackerInertiaRestingValue – 一种用于在交互或程序速度之后调整最终静止位置的机制。 预定义的动作会将 InteractionTracker 带到该位置。
  • InteractionTrackerInertiaMotion – 定义 InteractionTracker 在交互或编程速度之后执行的特定运动的方法。 最终位置将通过此运动得出。
  • InteractionTrackerInertiaNaturalMotion – 定义交互或编程速度之后的最终静止位置的机制, 并带有基于物理的动画(NaturalMotionAnimation)。

进入惯性时,InteractionTracker 会评估与其关联的每个惯性修饰符,并确定它们是否适用。 这意味着你可以创建多个 InertiaModifiers 并分配给 InteractionTracker,但是,在定义每个对象时,需要执行以下步骤:

  1. 定义条件 – 一个表达式,用于定义此特定 InertiaModifier 的执行条件。 这通常需要查看 InteractionTracker 的 NaturalRestingPosition(给定默认惯性的目标)。
  2. 定义静止值/动作/自然运动 – 定义满足条件时实际应用的静止值表达式、动作表达式或自然运动动画。

注释

当 InteractionTracker 进入惯性状态时,InertiaModifiers 的条件因素仅被评估一次。 但是,这仅适用于 InertiaMotion,此时运动表达式会对条件为 true 的修饰符每帧进行评估。

示例:

现在让我们看看如何使用 InertiaModifiers 创建一些对齐点体验来重新创建图像滚动画布。 在此示例中,每个操作都旨在可能移动单个图像 - 这通常称为“单个必需对齐点”。

首先,我们从初始化 InteractionTracker、VisualInteractionSource 开始,以及创建用于利用 InteractionTracker 位置的表达式。

private void SetupInput()
{
    _tracker = InteractionTracker.Create(_compositor);
    _tracker.MinPosition = new Vector3(0f);
    _tracker.MaxPosition = new Vector3(3000f);

    _source = VisualInteractionSource.Create(_root);
    _source.ManipulationRedirectionMode =
        VisualInteractionSourceRedirectionMode.CapableTouchpadOnly;
    _source.PositionYSourceMode = InteractionSourceMode.EnabledWithInertia;
    _tracker.InteractionSources.Add(_source);

    var scrollExp = _compositor.CreateExpressionAnimation("-tracker.Position.Y");
    scrollExp.SetReferenceParameter("tracker", _tracker);
    ElementCompositionPreview.GetElementVisual(scrollPanel).StartAnimation("Offset.Y", scrollExp);
}

接下来,由于单一强制对齐点行为会将内容向上或向下移动,因此需要两个不同的惯性修饰符:一个将滚动内容向上移动,另一个将其向下移动。

// Snap-Point to move the content up
var snapUpModifier = InteractionTrackerInertiaRestingValue.Create(_compositor);
// Snap-Point to move the content down
var snapDownModifier = InteractionTrackerInertiaRestingValue.Create(_compositor);

是否向上或向下对齐是根据 InteractionTracker 自然降落的相对于对齐距离(对齐点之间的距离)的位置来决定的。 如果超过过半点,则扣下,否则扣上。 (在此示例中,将对齐距离存储在 PropertySet 中)

// Is NaturalRestingPosition less than the halfway point between Snap Points?
snapUpModifier.Condition = _compositor.CreateExpressionAnimation(
"this.Target.NaturalRestingPosition.y < (this.StartingValue - " + 
"mod(this.StartingValue, prop.snapDistance) + prop.snapDistance / 2)");
snapUpModifier.Condition.SetReferenceParameter("prop", _propSet);
// Is NaturalRestingPosition greater than the halfway point between Snap Points?
snapDownModifier.Condition = _compositor.CreateExpressionAnimation(
"this.Target.NaturalRestingPosition.y >= (this.StartingValue - " + 
"mod(this.StartingValue, prop.snapDistance) + prop.snapDistance / 2)");
snapDownModifier.Condition.SetReferenceParameter("prop", _propSet);

此关系图提供正在发生的逻辑的直观描述:

惯性修饰符图

现在,您只需为每个 InertiaModifier 定义静止值:将 InteractionTracker 的位置移到上一个或下一个对齐位置。

snapUpModifier.RestingValue = _compositor.CreateExpressionAnimation(
"this.StartingValue - mod(this.StartingValue, prop.snapDistance)");
snapUpModifier.RestingValue.SetReferenceParameter("prop", _propSet);
snapDownModifier.RestingValue = _compositor.CreateExpressionAnimation(
"this.StartingValue + prop.snapDistance - mod(this.StartingValue, " + 
"prop.snapDistance)");
snapDownModifier.RestingValue.SetReferenceParameter("prop", _propSet);

最后,将 InertiaModifiers 添加到 InteractionTracker。 现在,当 InteractionTracker 进入惯性状态时,它将检查惯性修饰符的条件,以确定其位置是否需要修改。

var modifiers = new InteractionTrackerInertiaRestingValue[] { 
snapUpModifier, snapDownModifier };
_tracker.ConfigurePositionYInertiaModifiers(modifiers);