requestAnimationFrame
简介
requestAnimationFrame 是 HTM5 新增的一个定时器,它可以指定一个回调函数,要求浏览器在下次重绘之前调用指定的回调函数更新动画,并且时间间隔为一帧,能解决普通定时器时间间隔不稳定的问题。
可能看了简介之后还是不明白说了啥,在讲 requestAnimationFrame 之前先讲定时器
定时器
很多人可能会问:为什么不直接使用 setTimeOut() 和 setInterval() 来做动画呢?
首先,大多数浏览器屏幕刷新的时间间隔为 60Hz 即 1000/60 ms,那么 requestAnimationFrame 每隔16.67ms执行一次;如果屏幕刷新的时间间隔为 75Hz 即 1000/75 ms,那么 requestAnimationFrame 每隔13.33ms执行一次。这个时间间隔我们称为一帧。
然而:
(1)普通定时器的时间参数可任意指定,无论这个时间是大于一帧还是小于一帧,都跟页面的刷新频率不同步,体验感不佳。
(2)由于setTimeOut()和setInterval()是异步任务,定时器创建之后其回调函数会被放到异步队列当中去,当主线程任务执行完毕之后,浏览器才会去任务队列中检查是否有任务需要被执行,如果有就从队列当中取出并执行。因此,定时器设定的间隔时间 t 的含义其实是:间隔 t ms将回调函数放进异步任务队列,而不是间隔 t ms执行回调函数。综上所述,普通定时器的回调函数的延迟执行时间会比设定的时间晚。即使把时间调到了 16.67ms(以大部分浏览器的刷新频率为例)的情况,也不会刚好是 16.67ms 后执行回调函数。
那使用 requestAnimationFrame 有什么好处呢
(1)requestAnimationFrame 不能设置时间参数,其时间间隔不由JS控制,而是由系统时间间隔决定。如果屏幕刷新频率为16.67ms,则 requestAnimationFrame 每隔16.67ms立即执行一次;如果屏幕刷新频率为13.33ms,则其每隔13.33ms立即执行一次;
(2)由于使用了 requestAnimationFrame 后,其指定的回调函数会在浏览器下次重绘之前被调用,而requestAnimationFrame 的时间间隔又刚好为一帧,因此能保证回调函数在屏幕的每次刷新间隔中只被执行一次。
使用方法
(1)跟普通的定时器基本一致;
(2)若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()。
cancelAnimationFrame()
(1)作用是取消 requestAnimationFrame,相当于普通定时器当中的 clearTimeout(),clearInterval()。
(2)requestAnimationFrame 使用后会返回一个id,该id传递给 cancelAnimationFrame() 即可清除。
用法案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div class="testBox"></div>
</body>
<script>
const testBox = document.querySelector(".testBox");
testBox.style.width = "15px";
testBox.style.height = "20px";
testBox.style.backgroundColor = "pink";
testBox.onclick = () => {
let timer = requestAnimationFrame(function fn() {
if (parseInt(testBox.style.width) < 300) {
testBox.style.width = parseInt(testBox.style.width) + 3 + "px";
testBox.innerHTML = parseInt(testBox.style.width) / 3 + "%";
timer = requestAnimationFrame(fn);
} else {
cancelAnimationFrame(timer);
}
});
};
</script>
</html>