缓动函数和三角函数在前端动画开发中的应用

59,829 阅读9分钟

一、导言

前端开发中,动画实现方式有两种:CSS动画和JS动画,由于渲染方式的不同,CSS动画相对JS动画性能较好,故优先使用CSS来完成需要的动画效果,但是,在CSS动画无法实现(比如点击网站底部回到顶部)以及需要控制动画暂停、播放、弹跳、倒退或减速等场景下,我们就得使用JS动画,那如何通过JS编写一个符合自然运动规律、高性能的动画呢?下面,我会通过实现一个完整动画案例来一一展开介绍。

二、目标分解

我们的目的有两个:

  1. 符合自然运动规律
  2. 高性能

符合自然运动规律就是指动画应该符合现实物体运动规律,现实物体是按照一定节奏运动的,并不是一成不变的,比如你向空中抛一个球,球飞向空中的速度是先加速然后逐渐减速的,所以我们的动画应该是有生命力的,能够符合人们对现实现象认知的。

高性能就是指层现在人们眼睛里的画面无卡顿,画面始终以一个接近恒定的刷新频率(目标是60FPS)进行渲染的。因为根据人眼睛的视觉停留效应,若前一幅画像停留在大脑中的印象还没消失,后一幅画像接踵而至,并且两副画像间的差别很小,就会有“动”的感觉,所以我们的动画的渲染应该维持一个接近恒定的频率,这也是高性能动画的关键。

三、高性能动画

在动手实现我们的实际案例之前,我想而外介绍下高性能动画的关键点。了解浏览器渲染机制的童鞋应该都知道,在浏览器构建完DOM树以及CSSOM之后,就会进入到实际的渲染阶段,渲染一般经过重排、重绘和合成三个阶段。常见的移动元素(left、top等属性),变换元素大小等,都会导致元素重新经历重排和重绘和合成三个阶段,有关对指定的属性设置动画会触发哪个动作的详细信息,可以查看csstriggers

因而,要想动画性能高,我们就要尽量减少渲染路径,比如只触发重绘或者合成,从而提高动画性能。比如通过transform变换元素位置就会只触发合成阶段,因而性能较高。其它能够提升动画性能的方式有:

  1. 动画元素脱离文档流,防止动画导致其它元素重新渲染
  2. 尽量使用CSS3的transitionanimation实现动画(浏览器会有专门的合成器线程处理动画,不影响主线程上的任务)
  3. 使用will-change提前告诉浏览器单独开辟一个线程处理动画
  4. 使用类似transform``opacity等只触发合成阶段的属性实现动画效果
  5. 等等

四、三角函数与缓动函数

三角函数,学生时代肯定学过,是以角度(数学上最常用弧度制)为自变量,角度对应任意角终边与单位圆交点坐标或其比值为因变量的函数,在研究三角形和圆等几何形状的性质时有重要作用,也是研究周期性现象的基础数学工具。有点绕,不过我觉得只要知道通过它可以获取一组周期性数据,利用这组周期性数据,可以完成周期性动画或者生成周期性曲线即可,后面会有实际例子说明,更多关于三角函数的可以查看这篇文章

以正弦曲线为例,正弦曲线公式:y = A sin(Bx + C) + D,其中各个参数的意义如下:

A 控制振幅,A 值越大,波峰和波谷越大,A 值越小,波峰和波谷越小; B 值会影响周期,B 值越大,那么周期越短,B 值越小,周期越长。 C 值会影响图像左右移动,C 值为正数,图像右移,C 值为负数,图像左移。 D 值控制上下移动。

通过调整A、B、C、D值就可以得到我们想要的曲线效果,这个网站可以可视化查看各种三角函数的曲线效果。

缓动函数,区别于匀速运动,缓动函数给我们提供了随着时间变化按照非匀速变化运动的一种方式,点击这里可以查看各种不同的缓动曲线,github上流行的tween.js库就是一个具体实现的库。关于缓动函数的更多介绍可以查看如何使用Tween.js各类原生动画运动缓动算法

缓动函数主要用来调整动画的速率,如下方块从左到右,到底部反弹再回到底部,案例地址

可以看出,缓动函数能使动画看起来更自然并且符合现实运动规律,应该铭记于心的是我们在处理任何动画时都应该添加缓动效果,并且根据现实世界的一个潜意识规律定义缓动函数,使动画看起来更生动。

五、案例效果

普及完基础知识后,我们来讲下我们需要实现的案例,下面就是我们需要实现的动画效果,直播工具中常见的点击飘心动画。

这个案例功能很简单,就是点击桃心,随机出现不同颜色的桃心,以不规则的曲线向上飘动,然后逐渐消失,由于需要控制动画路径以及动态生成动画元素,CSS动画无法实现,故我们将会采用JS来实现。

六、具体实现

首先,我们需要监听点击事件,生成一个桃心元素,代码如下:

接着,生成的桃心以非直线路径向上飘动,这个路径的生成我们就可以使用三角函数来实现,我们这里用到了正弦、余弦、正切三种三角函数,分别对应左、中、右三个方向,这三个三角函数的生成返回的值的值域都是[-1,1],配合振幅、偏移量以及元素的基础位置等参数,我们就可以生成一个类似正弦、余弦、正切的路径曲线。

以正弦曲线为例,生成一个正弦曲线路径的代码如下,其中,deg * Math.PI / 180为角度转弧度,10代表振幅,注意这里振幅不能太大,否则会超出视图边缘,减2.5代表偏移量,可以自行调整。

生成三条路径曲线的代码:

接下来需要让桃心按照我们的路径曲线动起来,就需要随时调整元素的位置,首先我们想到的是使用setTimeout或者setInterval,按照1000 / 60 = 16.7ms间隔时间执行位移函数(FPS为60,平均绘制一帧需要16.7ms),然而由于浏览器中的页面循环系统(Event Loop)的原因,setTimeout的回调函数有可能不一定是按照我们指定的间隔时间执行的(任务队列里面可能掺杂其它微任务),具体原因读者可以自己去查阅,从而可能会导致动画渲染无法按照规律的刷新频率进行渲染,视觉上看起来就会有卡顿现象。

因此,浏览器提供了requestAnimationFrame来专门应对这种情况,它的用法等同于setTimeout,在下一个浏览器刷新周期执行回调函数,只不过每次的间隔时间由浏览器控制,不需要我们主动定义,因而渲染性能最高,使用js动画一般都是使用这个方法。

另外,动画位置偏移,我们使用tranform中的translate进行位移,而不是使用lefttop,原因是translate只会触发元素重新进入合成阶段,相比使用lefttop触发重绘、重排,资源消耗更低,因而性能更高。

桃心运动的代码如下:

此时,来看下我们的动画效果

同时看下性能,很好,FPS锯齿很平整,频率也接近60FPS,代表动画渲染性能较高。

大部分人做到这里估计就结束了,但是回想一下文章一开始说的,我们的目的是创建一个==高性能且符合自然运动规律==的动画,放到这里就是桃心向上飘动的过程速度应该是先加速然后不断降低的,而不是一成不变的。

故此,引入了缓动函数来调整速率,这里我们选择了缓出,它开头较快,使动画有反应快速的感觉,且结尾有个不错的减速,非常适合我们的场景,更多缓动动画函数查看easings,这里我们的目的是调整Y轴方向上速率,代码如下:

最后再看下我们动画效果,桃心向上飘动先加速后减速,非常自然。

至此,我们的动画才算完成。

七、总结

通过这篇文章,我介绍了三角函数和缓动函数在动画中的运用,指出了实现高性能动画的一些关键细节,以及如何去实现符合自然运动规律的动画,虽然个别地方不是很深入,但希望我的文章能够给你一个指引,在以后的工作开发中,碰到动画处理更加得心应手,知道该如何实现高效能且符合自然运动规律的动画。

以上完整代码可以在我的codepen查看,另外,访问我的github可以查看我的更多文章。

八、参考资料