在过去的几个月里,我一直积极地自学如何绘制和动画 SVG 形状。我一直在使用 CSS 过渡以及 D3.js、react-motion 和 GSAP 等工具来创建我的动画。
通常情况下,关于动画及其工具的文档都建议使用 **缓动函数**。这些年来我一直以某种方式使用它们,但说实话,我永远不知道应该为哪种动画选择哪个函数。此外,我不知道每个函数的魔力,它们之间的显著差异以及如何有效地使用它们。但我对此很满意,因为我知道缓动函数在某种程度上“平滑”了事物,并使我的作品看起来更加逼真。
在这里,我将向您介绍我学到的有关缓动函数的知识,以入门指南的形式,我希望它能为您深入了解动画提供很好的理解。
我如何接触到缓动函数
我尝试重新创建一个名为旋转蛇的图案,这是一种视觉错觉,它欺骗大脑以为圆圈在旋转和“跳舞”,而实际上它们并没有。

在尝试构建它时,我很快发现自己知识上的不足。这很难!但在过程中,我发现缓动函数在其中起着重要作用。
我转向 JavaScript,使用一个库在 SVG 中绘制了许多同心圆。
for (i = 1; i <= 10; i++) {
drawCircle({radius: i * 10});
}
这是结果

但这显然不像图片那样。
经过思考,我意识到自己想要一个特定的属性。我希望同心圆的半径变化在开始时很小,然后随着半径的增加而变大。
这意味着使用 i++
线性增加半径将不起作用。我们需要一个更好的公式来推导出半径。所以,我的下一次尝试看起来像这样
let i = 1;
let radiusList = [];
let radius = 0;
while (i <= 10) {
drawCircle({radius: i * 10});
if(i < 4) { i = i + 0.5 } else { i = i + 1 }
}
…这让我得到了这个

嗯,这仍然不是我想要的。事实上,这与图案的偏差更大。此外,这段代码几乎不可定制,难以维护。
所以我转向数学,尝试最后一次。
我们需要一个函数,它可以有机地和指数地改变半径。我突然灵机一动,也许你已经看到了。缓动函数可以做到这一点!
每个圆的半径应该在一开始缓慢增加,然后随着圆向外移动而快速增加。使用缓动,我们可以使事物沿着一条曲线移动,该曲线可以在某些点减速和加速。
快速谷歌搜索将我带到了 这个 gist,它是一个记录良好的缓动函数列表,真的救了我。每个函数接受一个输入值,运行公式,并提供一个输出值。输入值必须介于 0 和 1 之间。(我们将在后面深入探讨这个原因。)
二次缓动函数看起来很有希望,因为它所做的只是对它接收的值进行平方。
function (t) { return t*t }
这是我最终使用的代码
const easing = (t) => {
return t*t
}
for(i = 0; i<=1; i=i+0.05) {
const r = easing(i) * 40;
drawCircle(r);
}
我们找到了赢家!

这个图案与我的前两次尝试之间的区别是天壤之别。欢呼缓动函数!
这段小小的经历让我对缓动函数还能做什么产生了浓厚的兴趣。我在互联网上搜寻了有关它的酷炫信息。我找到了旧文章,大部分与 Flash 和 ActionScript 相关,这些文章有演示展示了不同的线形图。
这些都过时了,所以这是我关于缓动函数的小入门指南。
什么是缓动函数?
它们是一种函数,接受介于 0 和 1 之间的数值输入。该数字通过指定的函数运行,并返回另一个介于 0 和 1 之间的数字。**介于 0-1 之间的数值乘以另一个介于 0-1 之间的数值,始终得到一个介于 0-1 之间的数值。**这种特殊的性质帮助我们在特定边界内进行任何我们想要的计算。
缓动函数的目的是从 **线性值输入** 中获得 **非线性值**。
这是关于缓动函数我们需要知道的关键。从现在开始的所有解释和演示都将围绕这个概念展开。
缓动函数是 数学中的插值概念 的体现。插值是找到位于曲线上的点的集合的过程。缓动函数本质上是通过插值(计算)沿途的不同点集来从点 0 到点 1 绘制一条曲线。
Robert Penner 是第一个在他的 书 中定义缓动函数并创建不同缓动函数公式的人。
五种类型的缓动函数
有五种类型的缓动函数。它们可以混合、反转,甚至可以混合在一起形成更多复杂的函数。让我们深入研究每一种。
线性缓动函数
这是最基本的缓动形式。如果我们在 0 和 1 之间插值的点之间的间隔是恒定的,那么我们将形成一个线性缓动函数。
回到之前同心圆的例子,以恒定的量(在该例子中为 10px)增加初始圆的半径,会形成一个线性函数。

毫不奇怪,线性是默认的缓动函数。它们非常简单,因为动画没有曲线,物体以直线、一致的方向移动。也就是说,线性函数也有其缺点。例如,线性动画往往感觉不自然甚至很机械,因为现实生活中的物体很少以如此完美的直线运动。
二次缓动函数
二次缓动函数是通过将介于 0 和 1 之间的数值乘以自身(例如 0.5*0.5)来创建的。正如我们之前所学到的,我们看到这将得到一个也介于 0 和 1 之间的数值(例如 0.5*0.5 = 0.25)。
为了演示,让我们使用二次函数在 0 和 1 之间生成 10 个值。
const quad_easing = (t) => t*t;
let easing_vals = [];
for(let i = 0; i < 1; i +=0.1) {
easing_vals.push(quad_easing(i));
}
这是一个表格,包含我们得到的所有值
输入值(x 轴) | 二次缓动值(y 轴) |
---|---|
0 | 0 |
0.1 | 0.01 |
0.2 | 0.04 |
0.3 | 0.09 |
0.4 | 0.16 |
0.5 | 0.25 |
0.6 | 0.36 |
0.7 | 0.49 |
0.8 | 0.64 |
0.9 | 0.81 |
1 | 1 |
如果我们将这个值绘制在以 x 轴为原始值、y 轴为缓动值的图表上,我们将得到类似这样的东西

注意到什么了吗?这条曲线与我们通常在 CSS 中看到的 ease-in 函数几乎一样!
三次、四次和五次缓动函数
最后三种类型的缓动函数行为相同,但使用不同的值。
三次缓动函数是通过将介于 0 和 1 之间的数值乘以自身三次来创建的。换句话说,它是一些值(例如 t),立方(例如 t3)。
四次函数做同样的事情,但为 4 次方。所以,如果 t 是我们的值,我们正在查看 t4
正如你已经猜到的,五次函数运行到 5 次方。
以下演示将为您提供一种方法来玩转五种类型的函数,以便直观地了解它们之间的区别。
查看 CodePen 上 Pavithra Kodmad (@pkodmad) 的 绘制缓动函数 Pen。
缓入和缓出…或者两者!
“缓入缓出是一种美味的半半结合,就像香草巧克力漩涡冰淇淋甜筒。”
— Robert Penner
缓入和缓出可能是最常见的缓动动画。它们通常通过在动画的开始或结束(或两者!)减速来平滑典型的线性线条。
缓入和缓出动画可以使用我们已经看过的任何非线性函数来创建,尽管三次函数最常使用。事实上,CSS animation
属性通过 animation-timing-function
子属性,直接提供了 ease-in
和 ease-out
值。
ease-in
:此函数开始缓慢,但结束更快。ease-out
: 此函数开始时速度快,结束时速度慢。ease-in-out
: 此函数是其他函数的组合,开始时速度快,中间速度慢,然后结束时速度快。
查看 CodePen 上 Pavithra Kodmad 的 缓动演示 (@pkodmad)。
可以在这里 cubic-bezier.com 上试用它们。
这些曲线也可以在 JavaScript 中创建。我个人喜欢并使用 bezier-easing 库来实现它。 Easing.js 也是一个不错的选择,还有 D3 库(来自 Mike Bostock 的 不错的示例)。如果你更喜欢 jQuery,可以看看 这个插件 甚至 这个插件。
看到了吧,真的很“容易”!
我希望这个简短的入门介绍能够帮助说明缓动函数和插值的概念。这些函数可以使动画更加自然和逼真,方法很多。可以查看 Easing.css,它提供一个用户界面,可以创建自定义曲线,并附带大量预设选项。
我希望下次你使用缓动函数时,它不再是一个黑盒子。你现在拥有了一个基本了解,可以将缓动函数付诸实践,并在处理动画时打开更多可能性。
更多关于缓动的内容
我们在这里只触及了缓动函数的表面,但 CSS-Tricks 上还有其他很好的资源值得一阅,可以让你更上一层楼。
缓动真有趣!GSAP 有很多独特的与缓动相关的工具,值得探索
使用 SVG 路径本身制作缓动!可以在浏览器中直接绘制/拖动,无限点。 https://greensock.com/customease
摆动和弹跳(带压缩和伸展):https://greensock.com/wiggle-bounce
平滑地放大或缩小事物在缓动方面提出了一个独特的挑战:https://greensock.com/docs/Easing/ExpoScaleEase
感谢您撰写这篇文章!做得好。
盯着旋转的蛇,试图让它们停下来。
做不到。
眼睛流血地读完这篇文章 :-D
这不是我做过的最明智的事。