前言
在相当长的一段时间里,定时器工具是JavaScript中最常使用的动画工具,但是该方式作为动画的实现手段存在其原理上的弊端,直到requestAnimationFrame()这个API的出现,可以使浏览器以最优的时机进行重绘,自此后,requestAnimationFrame已经成为前端动画实现的最佳选择。
浏览器动画原理
要真正理解requestAnimationFrame的优势首先要清楚浏览器动画的基本实现思路。实际上就是在前端运行一个频繁触发的函数,这个函数每次运行时都是更新物体的位置属性,比如每次都沿X方向每次都会加1,当这个函数的触发频率足够高,人眼就不会有任何察觉,因此显示在屏幕上的效果就是流畅的动画了。在requestAnimationFrame之前,最常用的是setInterval定时器,结合canvas,实现每15毫秒移动一次,效果是这样的:
setInterval(() => {
translate += 0.005;
if (translate > 1) {
translate = 0
}
gl.vertexAttrib1f(aTranslate, translate)
gl.drawArrays(gl.POINTS, 0, 1)
}, 15);
setInterval实现的动画效果如上,但是明显看到它并不流畅,这是它的设计缺陷决定的:虽然我们在代码中每隔15ms更改一次图形的位置,但是浏览器执行频率是存在极限的,一般来说浏览器的刷新频率是每秒60次,也就是每隔大约16ms执行一次,在这个频率之上即便高再多人眼也没有感知,但是为了满足尽可能使每次浏览器刷新时都能执行到我们的函数,要把时间间隔设置的尽量小,因为setInterval函数无法保证和浏览器刷新同时执行,间隔过大时在容易把浏览器“套圈”,造成丢帧现象,而间隔设置的过小又会提高运行负担,影响效率。
requestAnimationFrame()——更丝滑的动画实现
与setInterval的缺点相对应,requestAnimationFrame动画完全避免了setInterval的缺点,首先,它的执行时机完全和浏览器的刷新时机完全一致,其次,由于requestAnimationFrame是动画API,在页面销毁或隐藏时它会自动停止执行,节约了浏览器的开销。该API的使用方法是回调:
function animate() {
console.log('setInterval')
translate += 0.005;
if (translate > 1) {
translate = 0
}
gl.vertexAttrib1f(aTranslate, translate)
// 开始绘制
gl.drawArrays(gl.POINTS, 0, 1)
requestAnimationFrame(() => animate())
}
animate()
若需要销毁定时器,使用cancelAnimationFrame即可:
let timer
function animate() {
console.log('setInterval')
translate += 0.005;
if (translate > 1) {
translate = 0
}
gl.vertexAttrib1f(aTranslate, translate)
// 开始绘制
gl.drawArrays(gl.POINTS, 0, 1)
timer = requestAnimationFrame(() => animate())
}
// 销毁定时器
function removeAnimate(){
cancelAnimationFrame(timer)
}
总结
浏览器动画原理
- 浏览器动画原理
- setInterval不足
requestAnimationFrame()——更丝滑的动画实现
- requestAnimationFrame优势
- requestAnimationFrame使用