反转缓动曲线

Avatar of Michelle Barker
Michelle Barker

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费信用额度!

让我们来看一下我处理的一个轮播,其中项目使用 CSS 动画滑入和滑出视图。为了使每个项目都能很好地滑入和滑出视图,我使用了三次贝塞尔曲线作为 animation-timing-function 属性,而不是使用标准的缓动关键字。

查看 CodePen 上 Michelle Barker (@michellebarker) 编写的 带有反转缓动曲线的轮播

三次贝塞尔曲线乍一看可能令人困惑,但如果使用得当,它可以为用户体验增添一抹亮色。

在构建此轮播时,我意识到我不仅需要自定义动画曲线,而且还需要反向使用它才能获得正确的效果。我认为我学到的东西值得分享,因为创建自定义曲线并将其反转可能看起来很棘手,但实际上非常简单。

首先,缓动基础知识

缓动 是用来描述动画在时间轴上的加速和减速的术语。我们可以将其绘制成图表,其中 x 是时间,y 是动画的进度。线性动画(没有加速或减速,始终以相同的速度移动)的图表是一条直线。

Linear easing graph

非线性缓动使动画具有更自然、更逼真的感觉。我们可以在 CSS 中将缓动应用于过渡和动画。animation-timing-function 属性允许我们定义动画的缓动。(transition-timing-function 也提供了相同的选项。)我们可以从中选择四个关键字。

  • linear – 如上所述
  • ease-in – 动画开始时缓慢,然后随着进度加快
  • ease-out – 动画开始时快速,然后在结束时减速
  • ease-in-out – 动画开始时缓慢,中间加速,然后在结束时减速
  • ease – 默认值,ease-in-out 的变体
  • 了解三次贝塞尔曲线

    如果这些选项都不适合我们的动画,我们可以使用 cubic-bezier 函数创建自定义缓动曲线。这是一个示例。

    .my-element {
      animation-name: slide;
      animation-duration: 3s;
      animation-timing-function: cubic-bezier(0.45, 0.25, 0.60, 0.95);
    }

    如果我们愿意,可以将这些属性写成简写形式,如下所示。

    .my-element {
      animation: slide 3s cubic-bezier(0.45, 0.25, 0.60, 0.95);
    }

    您会注意到 cubic-bezier 函数需要四个值。这是将曲线绘制到图表上所需的两个坐标对。这些坐标代表什么?好吧,如果您使用过矢量插图程序(如 Illustrator),那么您可能熟悉矢量点和控制曲线大小和方向的“控制柄”的概念。这本质上是我们使用三次贝塞尔曲线绘制的内容。

    我们不需要了解三次贝塞尔曲线背后的所有数学知识就可以创建漂亮的动画。幸运的是,有很多在线工具(例如 Lea Verou 的 cubic-bezier.com)可以让我们可视化缓动曲线并复制值。这就是我为上述缓动曲线所做的,它看起来像这样。

    Cubic-bezier easing graph

    在这里,我们可以看到我们需要绘制的两个点,其中三次贝塞尔曲线函数为 cubic-bezier(x1, y1, x2, y2)

    Cubic-bezier easing graph showing co-ordinates

    双向应用缓动

    我的轮播可以双向旋转——如果单击左箭头,当前项目将向右滑出视图,下一个项目将从左侧滑入;如果单击右箭头,则反之亦然。为了使项目滑入或滑出视图,根据用户单击“下一个”或“上一个”按钮,会向每个项目添加一个类。我最初的假设是,我可以简单地反转 animation-direction 以使项目向相反方向滑出,如下所示。

    .my-element--reversed {
      animation: slide 3s cubic-bezier(0.45, 0.25, 0.60, 0.95) reverse;
    }

    只有一个问题:反转动画也反转了缓动曲线!所以,现在我的动画在一个方向上看起来很棒,但在另一个方向上完全不对劲。哦,不!

    在此演示中,第一个框显示初始动画(项目从左向右滑动),第二个框显示反转 animation-direction 后发生的情况。

    查看 CodePen 上 Michelle Barker (@michellebarker) 编写的 反转动画

    您可以看到这两个动画的感觉完全不同。第一个框在早期加速,然后随着进度的缓慢减速,而第二个框开始时相当迟缓,然后获得一阵速度,然后突然减速。对于轮播来说,这感觉完全不对。

    我们有两种选择可以实现这一点。

    1. 我们可以创建一个新的关键帧动画来使项目滑出,然后应用与之前相同的缓动。在这个例子中,这并不难,但如果我们的动画更复杂呢?要反向创建相同的动画需要花费更多工作,并且我们很容易犯错。
    2. 我们可以使用相同关键帧动画(使用 animation-direction: reverse)并反转缓动曲线,以便在两个方向上获得相同的缓动。这也不难做到。

    要反转反向动画的缓动曲线,我们需要将其围绕其轴旋转 180 度并找到新的坐标。

    Comparing original easing graph with reversed graph

    我们可以使用一些简单的数学方法来做到这一点——通过交换坐标对并将每个值减 1。

    为了可视化这一点,假设我们的原始值为

    x1, y1, x2, y2

    我们的反转值将为

    (1 - x2), (1 - y2), (1 - x1), (1 - y1)

    在此演示中,第三个框显示了我们想要发生的事情:项目向相反方向滑动,但缓动被反转以使其具有相同的感觉。

    查看 CodePen 上 Michelle Barker (@michellebarker) 编写的 使用 CSS 变量反转缓动

    让我们逐步了解如何计算反转缓动曲线。

    使用 CSS 变量计算新曲线

    我们可以使用 CSS 变量来帮助我们计算新曲线!让我们将每个值分配给一个变量。

    :root {
      --x1: 0.45;
      --y1: 0.25;
      --x2: 0.6;
      --y2: 0.95;
      
      --originalCurve: cubic-bezier(var(--x1), var(--y1), var(--x2), var(--y2));
    }

    然后我们可以使用这些变量来计算新值。

    :root {
      --reversedCurve: cubic-bezier(calc(1 - var(--x2)), calc(1 - var(--y2)), calc(1 - var(--x1)), calc(1 - var(--y1)));
    }

    现在,如果我们对第一组变量进行任何更改,反向曲线将自动计算。为了在检查和调试代码时更容易扫描,我喜欢将这些新值分解成它们自己的变量。

    :root {
      /* Original values */
      --x1: 0.45;
      --y1: 0.25;
      --x2: 0.6;
      --y2: 0.95;
      
      --originalCurve: cubic-bezier(var(--x1), var(--y1), var(--x2), var(--y2));
      
      /* Reversed values */
      --x1-r: calc(1 - var(--x2));
      --y1-r: calc(1 - var(--y2));
      --x2-r: calc(1 - var(--x1));
      --y2-r: calc(1 - var(--y1));
      
      --reversedCurve: cubic-bezier(var(--x1-r), var(--y1-r), var(--x2-r), var(--y2-r));
    }

    现在剩下的就是将新曲线应用于反向动画。

    .my-element--reversed {
      animation: slide 3s var(--reversedCurve) reverse;
    }

    为了帮助可视化这一点,我构建了一个小工具来计算三次贝塞尔曲线的反转值。输入原始坐标值以获取反转曲线。

    查看 CodePen 上 Michelle Barker (@michellebarker) 编写的 反转三次贝塞尔曲线