使用运动曲线升级 CSS 动画
已发表: 2022-03-10transition-timing-function
提供了一些预设,例如ease-in
、 ease-out
和ease-in-out
,它们增加了一定程度的平滑度和真实感,但它们非常通用,不是吗? 如果网络上的每个动画都遵循相同的三个计时功能,那该有多无聊?有UI动画,再有好的UI动画。 好的动画会让你“哇!” ——它光滑、美丽,最重要的是,自然,而不是块状、僵硬或机器人。 如果您经常使用 Dribbble 或 UpLabs,您就会知道我在说什么。
关于粉碎的进一步阅读:
- 带有剪辑路径的 SVG 和 CSS 动画
- 实用动画技术
- 使用 SVG 创建“手绘”动画
- 新的网络动画 API
有这么多出色的设计师创造了如此精美的动画,任何开发人员自然都希望在自己的项目中重新创建它们。 现在,CSS 确实为transition-timing-function
提供了一些预设,例如ease-in
、 ease-out
和ease-in-out
,它们增加了一定程度的平滑度和真实感,但它们非常通用,不是吗? 如果网络上的每个动画都遵循相同的三个计时功能,那该有多无聊?
transition-timing-function
的属性之一是cubic-bezier(n1, n2, n3, n4)
,您可以在其中传递四个数字来创建您自己的计时函数。 在本文的结尾,您将确切地知道这四个数字代表什么——不过,相信我,想出四个数字来捕捉您在脑海中想象的过渡绝非易事。 但是多亏了cubic-bezier
和Ceasar,您不必这样做。 这些工具将运动曲线带入网络。
动画师(例如,在 Adobe After Effects 中)主要使用运动曲线来创建高级、逼真的动画。 使用cubic-bezier
和Ceasar,您可以简单地操纵曲线的形状,这四个数字( n1, n2, n3, n4
)将为您填充,这绝对很棒! 尽管如此,要使用并充分利用运动曲线,您需要了解它们的工作原理,这就是我们将在本文中做的事情。 让我们开始。
了解运动曲线
运动曲线只不过是任何可动画属性和时间之间的曲线。 运动曲线定义了在其影响下运行的动画的速度如何随时间变化。

让我们以距离( translateX
)作为动画属性的示例。 (该解释适用于任何其他可动画属性。)

如果您有任何物理和基本微积分的经验,您就会知道从距离-时间图表中破译速度非常简单。 作为时间函数的距离的一阶导数是速度,这意味着遵循距离-时间曲线的物体在曲线陡峭的地方速度更快,而在曲线更平坦的地方速度更低. 如果你知道它是如何工作的,那就太好了! 一切就绪,可以跳到下一部分。
现在,我意识到设计和开发是一个多元化的领域,并不是每个人都有相同的背景。 也许上面两段对你来说都是行话。 别担心。 我们将继续前进并理解行话。
考虑下面的红色框。 让我们在这里得到一个小草包,将红色框称为“Boxy”; 这样引用它会更容易。 好的,所以 Boxy 将以线性方式从屏幕的一个边缘移动到另一个边缘,我们将分析它的运动。
transition-timing-function
的预设之一是linear
。 为了让 Boxy 移动,我们所做的就是添加以下类。
.moveForward { transform: translateX(1000px); }
为了控制动画,我们将设置 Boxy 的transition
属性如下:
#boxy { width: 200px; height: 200px; background: red; transition-property: transform; transition-duration: 1s; transition-timing-function: linear; }
这是指定transition
的一种非常冗长的方式。 实际上,您几乎总能找到以简写形式编写的transition
:
#boxy { width: 200px; height: 200px; background: red; transition: transform 1s linear; }
让我们看看吧。

机器人,不是吗? 你可以说这个动作感觉机器人,因为它是线性的,这是一个完全合理的答案。 但是你能解释一下为什么吗? 我们可以看到设置linear
会导致机器人运动,但幕后究竟发生了什么? 这就是我们首先要弄清楚的; 我们将深入了解内部结构并理解为什么这个动作会让人感觉机器人、块状和不自然。
让我们从绘制 Boxy 的运动图开始,看看我们能否获得一些见解。 我们的图表将有两个轴,第一个是距离,第二个是距离。 Boxy 在 1 秒(时间)内覆盖了 1000 个像素(距离)的总距离。 现在,不要被下面的所有数学吓到——它很简单。
这是我们非常简单的图表,其中提到了轴。

现在,它是空的。 让我们用一些数据来填充它。
首先,我们知道在 0 秒时,当动画尚未开始时,Boxy 处于其初始位置(0 像素)。 1 秒过去后,Boxy 总共移动了 1000 个像素,落在了显示屏的另一边。

让我们在图表上绘制这些数据。

到目前为止,一切都很好。 但是两个数据点是不够的——我们需要更多。 下图显示了 Boxy 在不同时间点的位置(这都归功于我的高速相机)。

让我们将此数据添加到我们的图表中。

当然,您可以拥有更多不同时间的数据点(例如,0.375 秒、0.6 秒等),但我们所拥有的足以完成我们的图表。 通过连接所有点,我们完成了图表。 击掌!

很酷,但这告诉我们什么? 还记得我们开始调查的目的是了解为什么 Boxy 的直线运动会让人感觉不自然和机械化吗? 乍一看,我们刚刚构建的这张图并没有告诉我们任何关于它的信息。 我们需要更深入。
记住图表,让我们谈谈速度。 我知道你知道什么是速度——我只想用数学术语来表达。 事实上,速度的公式是这样的:

因此,如果一辆汽车在 1 小时内行驶了 100 公里,我们说它的速度是每小时 100 公里。

如果汽车速度加倍,它会在相同的时间间隔(1 小时)内开始行驶两倍的距离(200 公里),或者换句话说,它将在一半的时间(0.5 小时)内行驶原来的 100 公里距离. 有道理?
同样,如果汽车将速度减半(即减速一半),它会在相同的时间间隔(1 小时)内开始行驶 50 公里的距离,或者换句话说,它会行驶原来的 100 公里。公里的两倍时间(2 小时)。
伟大的! 有了这些,让我们从上次停下的地方继续。 我们试图弄清楚距离和时间之间的图表如何帮助我们理解为什么 Boxy 的线性运动感觉像机器人一样。
嘿,等一下! 我们有距离和时间之间的图表,可以从距离和时间计算速度,不是吗? 让我们尝试计算 Boxy 在不同时间间隔的速度。

在这里,我选择了三个不同的时间间隔:一个靠近开始,一个在中间,一个在结束,靠近最终位置。 很明显,在所有三个间隔中,Boxy 的速度 (s1 = s2 = s3) 完全相同,为每秒 1000 个像素; 也就是说,无论您在上图中选择什么间隔,您都会发现 Boxy 以每秒 1000 像素的速度移动。 这不是很奇怪吗? 现实生活中的事物不会以恒定的速度移动。 他们慢慢地开始,逐渐加快速度,移动了一会儿,然后再次减速,然后停止,但Boxy突然以每秒1000像素的速度开始,以相同的速度移动,然后以完全相同的速度突然停止。 这就是为什么 Boxy 的动作感觉机器人和不自然的原因。 我们将不得不改变我们的图表来解决这个问题。 但在深入研究之前,我们需要知道速度的变化将如何影响在距离和时间之间绘制的图表。 准备好? 这将会非常好玩。
让我们将 Boxy 的速度加倍,看看图形的外观如何响应变化。 Boxy 的原始速度,正如我们上面计算的,是每秒 1000 像素。 因为我们将速度提高了一倍,Boxy 现在能够在一半的时间内覆盖 1000 个像素的距离——即 0.5 秒。 让我们把它放在图表上。

如果我们将速度提高三倍呢? Boxy 现在可以在三分之一的时间内(三分之一秒)覆盖 1000 个像素。


嗯,注意到什么了吗? 请注意,当图形发生变化时,线与时间轴的夹角如何随着速度的增加而增加。
好吧,让我们继续将 Boxy 的速度减半。 将其速度减半意味着 Boxy 将只能在 1 秒内覆盖 500 个像素(原始距离的一半)。 让我们把它放在图表上。

让我们再把 Boxy 放慢一点,把速度变成原来的三分之一。 Boxy 将能够在 1 秒内走完原始距离的三分之一。

看到图案了吗? 随着我们增加 Boxy 的速度,这条线变得越来越陡峭,而随着我们放慢 Boxy 的速度,这条线开始变平。

这是有道理的,因为对于更陡峭的线路,时间上的一点进步会产生更大的距离变化,这意味着更快的速度。


另一方面,对于不太陡峭的线路,时间的较大变化只会产生很小的距离变化,这意味着较低的速度。


通过我们所做的所有更改,Boxy 仍然以线性方式移动,只是速度不同。 然而,凭借我们新获得的关于距离随时间变化如何影响速度的知识,我们可以试验并绘制一个图表,使 Boxy 以一种看起来自然而逼真的方式移动。
让我们一步一步来。 首先,现实生活中的事情一开始很慢,而且速度会慢慢增加。 所以,让我们这样做。
在下图所示的所有迭代中,您会注意到对角处的点保持固定。 这是因为我们没有改变动画运行的持续时间,也没有改变 Boxy 行进的距离。

如果 Boxy 是按照上图,它会以较慢的速度移动 0.25 秒,因为线从 0 到 0.25 秒开始不那么陡峭,然后在 0.25 秒后突然切换到更高的速度(原因是图中的线在 0.25 秒后变得更陡峭)。 不过,我们需要使这种过渡更加顺畅; 我们不想要任何角落——毕竟它被称为运动曲线。 让我们将该角转换为曲线。

注意 Boxy 从静止到逐渐增加速度的平滑过渡。

好的! 接下来,现实生活中的物体在停止之前会逐渐减速。 让我们更改图表以实现这一点。 同样,我们将选择一个时间点,之后我们希望 Boxy 开始减速。 大约0.6秒怎么样? 我已经将过渡的拐角平滑到这里的曲线。

看看 Boxy 去吧! 更自然,不是吗?

我们画的代替角的曲线实际上是许多小线段的集合; 而且,正如您已经知道的那样,图表上的线越陡,速度就越高,而线越平坦,速度越慢。 请注意,在图像的左侧,构成曲线的线段如何变得越来越陡峭,导致速度逐渐增加,而在右侧逐渐变平,导致速度逐渐降低?

有了所有这些知识,理解运动曲线就变得容易多了。 让我们看几个例子。






在 UI 动画中使用运动曲线
下次您必须为 UI 元素设置动画时,您将拥有运动曲线的力量。 无论是滑出栏、模态窗口还是下拉菜单,添加适量的动画并使其看起来流畅自然将大大提高用户界面的质量。 它将使用户界面感觉良好。 采取下面的滑出式菜单:
请参阅 CodePen 上 Nash Vail (@nashvail) 的 Pen nJial。
单击汉堡菜单会从左侧进入菜单,但动画感觉是块状的。 CSS 的第 51 行显示动画已将transition-timing-function
设置为linear
。 我们可以改进这一点。 让我们继续使用三次贝塞尔曲线并创建一个自定义计时函数。
如果您正在阅读本文,可以肯定地假设您是设计师或开发人员或两者兼而有之,因此对三次贝塞尔曲线并不陌生; 你很有可能至少遇到过一次。 贝塞尔曲线是一个奇迹。 它们主要用于计算机图形中以绘制形状,并在 Sketch 和 Adobe Illustrator 等工具中用于绘制矢量图形。 三次贝塞尔曲线如此受欢迎的原因在于它们非常易于使用:只需修改四个不同点的位置,然后创建您需要的曲线类型。
因为我们总是知道动画对象的初始和最终状态,所以我们可以修复其中的两个点。 只剩下两个点,我们必须修改它们的位置。 两个固定点称为锚点,剩下的两个称为控制点。

您还记得,当您创建自定义transition-timing-function
时, cubic-bezier
接受四个数字( n1, n2, n3, n4
)。 这四个数字只代表两个控制点的位置: n1, n2
表示第一个控制点的 x 和 y 坐标, n3, n4
表示第二个控制点的坐标。 因为改变控制点的位置会改变曲线的形状,从而改变我们的整体动画,当修改n1, n2, n3, n4
中的任何一个或全部时,结果是相同的。 例如,下图表示cubic-bezier(.14, .78, .89, .35)
:

(.14, .78, .89, .35)
的三次贝塞尔曲线(查看大图)这些看似简单的曲线背后的数学原理令人着迷。
好吧,好吧,让我们回到我们使用三次贝塞尔曲线的地方:创建一个自定义的transition-timing-function
。 我想要一种动画,其中菜单非常快速地滑入,然后优雅地放慢速度并结束:

这看起来不错。 动画将开始快速然后减速,而不是始终以恒定速度移动。 我只是要从页面顶部复制cubic-bezier(.05, .69, .14, 1)
并用它替换linear
。
请参阅 CodePen 上 Nash Vail (@nashvail) 的 Pen nJial。
看到不同? 第二次迭代感觉更加自然和吸引人。 想象一下,如果你的 UI 中的每个动画都遵循一个自然的计时函数。 那该有多好?
正如我们所见,运动曲线一点也不棘手。 它们非常容易理解和使用。 有了它们,您可以将您的 UI 提升到一个新的水平。
我希望您已经了解了运动曲线的工作原理。 如果您经历了很多尝试和错误以使运动曲线以您想要的方式工作,或者如果您根本没有使用它们,那么您现在应该可以根据自己的意愿弯曲它们并创建漂亮的动画。 因为,毕竟,动画很重要。