对于
web
应用来说,实现动画的方式有很多。在CSS3
的动画效果已经非常强大的前提下,我们一般是不推荐使用JS
来实现动画效果的,一是因为JS
动画通常性能上都非常不乐观,二是写起来也十分麻烦,而且不易维护。但在某些场景下,当
CSS3
动画不能满足需求时,我们就不得不借助于JS
来实现了。在以前我们一般使用setTimeout
通过定时来控制元素的位置,角度,透明度,显隐等属性实现动画的过渡效果。今天要介绍的主角
requestAnimationFrame
,通过名字也可以看出,它与动画有关(废话!!要不我上面扯半天动画干嘛)。那么requestAnimationFrame
相比于setTimeout
又有哪些突出的优势呢?让我们通过这篇博客来一一解释。
动画
大家一定还记得,小时候我们看电影,放映员都是拿着一大盘的胶片,装在投影灯上来播放电影的,这也就是老式的胶片电影。通过将电影的一幕幕画面先洗在胶片上,然后将胶片快速的从投影灯前移动来打到大影幕上,从而在荧幕上显出连贯的画面来。胶片上的一个画面,就叫做一帧。
科技在发展,目前的显示器已经从老式的电子管到液晶,再到LED
等材料,但不管显示器的科技如何更新,它基本的原理仍然是利用人眼的暂留效应,通过快速呈现出一帧帧静止的画面来实现基本的动态显示的。这个快速有多块呢?一般来说,普通显示器的显示频率是60Hz,也就是每16.7ms(1000/60)一帧。当然,现在有很多显示器早已达到了144HZ
甚至更高。
再回到我们的正题,动画其实归根结底,也是更新每一帧的对应位置,从而形成一系列连贯平滑的屏幕显示图像,在人眼中表现出流畅的过渡效果。
实现动画的几种方式:
在网页中,我们实现动画主要有以下几种方式:
html5
的canvas
css3
的transition
和animation
JavaScript
的setTimeout
具体的来说,这几种方式都有相应的优势和局限性。
html5的
canvas
canvas
的功能相当强大,可以实现很多酷炫的效果。但因为它本身是用来提供web
的扩展支持和自主绘制的,除非在某些特定领域,大部分时间里对于我们平常需要动画效果来说,是不会用到它的。
css3的transition和animation
CSS3
动画的优点在于实现简单,维护容易,可复用性高,同时也可以挖掘出很多相当强大的动画效果。所以一般的,我们都推荐使用CSS3
来进行网页的动画开发。
但在某些场景下,使用CSS3
是无法达到我们的需求的。典型的有以下两个场景:
需要逻辑判断和复杂的前置条件处理的动画,例如与元素的
scrollTop
值相关的动画,例如根据不同条件进行不同表现的动画。非标准曲线的动画,因为
CSS3
只支持标准的贝塞尔曲线,所以它是无法实现很多复杂特殊的动画效果曲线的。
对于以上两种不适合CSS3
的动画,我们通常会使用JS
来实现。那下面我们就来看一下setTimeout
的优缺点。
setTimeout
setTimeout
通过设定一个时间间隔来不断的更新屏幕图像,从而完成动图。
它的优点是可控性高,可以进行编码式的动画效果实现。
但是有以下几个缺点:
- 执行时间因为
JS
的线程和事件循环问题会不准确 - 执行的时间间隔是固定的,在屏幕刷新的两帧间隔中无论
setTimeout
的回调函数执行了几次,都只会有最后一次有用,显示器只会更新最后一次执行结果对应的图像。
造成无用的函数运行开销,也就是过度绘制,同时因为更新图像的频率和屏幕的刷新重绘制步调不一致,会产生丢帧,在低性能的显示器动画看起来就会卡顿。 - 当网页标签或浏览器置于后台不可见时,仍然会执行,造成资源浪费。
而requestAnimationFrame
相比setTimeout
,比较好的解决了以上的几个缺点。
requestAnimationFrame
翻译过来就是请求动画帧,简称rAF
,它是浏览器全局对象window
的一个方法。
相比于setTimeout
的在固定时间后执行对应的动画函数,rAF
用于指示浏览器在下一次重新绘制屏幕图像时, 执行其提供的回调函数。
这也是rAF
的最大优势–它能够保证我们的动画函数的每一次调用都对应着一次屏幕重绘,从而避免setTimeout
通过时间定义动画频率,与屏幕刷新频率不一致导致的丢帧。
它的另一个优点就是在页面被置于后台或隐藏时,会自动的停止,不进行函数的执行,当页面激活时,会重新从上次停止的状态开始执行,因此在性能开销上也会相比setTimeout
小很多。
总的来说,requestAnimationFrame
的优点就是:
- 使浏览器画面的重绘和回流与显示器的刷新频率同步
- 节省系统资源,提高性能和视觉效果
详细语法
它的基本用法类似于setTimeout
如下:
1 | let i = 0 |
在每一次屏幕显示图像的更新中,都将元素向左移动1px
。
具体语法如下:
window.requestAnimationFrame(callback)
requestAnimationFrame
方法接收一个函数参数callback
,这个函数参数会在浏览器下次绘制前执行。callback
函数会被传入一个参数DOMHighResTimeStamp
,这个参数的含义是指callback
的执行时间,也就是下一次屏幕绘制发生的时间。requestAnimationFrame
方法返回一个长整数,作为本次方法调用的唯一标志ID
,可以将这个ID
传递给window.cancelAnimationFrame()
来取消掉对应的回调函数。
兼容问题
目前的时间点上,几乎所有的浏览器现行版本都支持了rAF
函数。但在一部分浏览器上还需要加上兼容性前缀。
下面是一个比较简单的兼容polyfill
函数用来使requestAnimation
兼容各浏览器:
1 | window.requestAnimationFrame = (function(){ |
在上面的代码中,当浏览器不支持此API
时,我们代替的使用了setTimeout
来保证了方法的向后兼容。
当然,这个polyfill
没有考虑到需要连带使用cancelAnimationFrame
的情况。如果有需要的同学,可以自己去搜索一下,在此就不再多言。
OK,关于requestAnimationFrame
的相关内容,比较简单,就介绍到这里,多谢阅读。