requestAnimationFrame介绍
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
语法:
window.requestAnimationFrame(callback);
参数:
callback
: 下一次重绘之前更新动画帧所调用的函数(即上面所说的回调函数)。该回调函数会被传入DOMHighResTimeStamp参数,该参数与performance.now()的返回值相同,它表示requestAnimationFrame()
开始去执行回调函数的时刻。
返回值:
一个
long
整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。你可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。
如何使用?
<html>
<head></head>
<body>
<button id="start">开始</button>
<button id="stop">停止</button>
<script>
window.onload = function () {
function test() {
console.log("requestAnimationFrame ----");
}
requestAnimationFrame(test);
}
</script>
</body>
</html>
可以看到,控制台成功的输出了一次 。
但是它只执行了一次,怎么做动画呢?别急,再看看 MDN
怎么说。
注意:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用
window.requestAnimationFrame()
改下JS
window.onload = function () {
var n = 0;
function test() {
n++;
console.log("requestAnimationFrame ----", n);
requestAnimationFrame(test);
}
requestAnimationFrame(test);
}
执行频率
回调函数执行次数通常是每秒60次,但在大多数遵循W3C建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。
即不用手动设置执行间隔时间,而是根据 浏览器屏幕刷新次数 自动调整了,也就是说浏览器屏幕刷新多少次,它就执行多少次。
回调函数参数
在同一个帧中的多个回调函数,它们每一个都会接受到一个相同的时间戳,即使在计算上一个回调函数的工作负载期间已经消耗了一些时间。该时间戳是一个十进制数,单位毫秒,最小精度为1ms(1000μs)。
function test(timeStamp) {
console.log("requestAnimationFrame ----", timeStamp);
requestAnimationFrame(test);
}
requestAnimationFrame(test);
可以看出每次执行隔间时间戳近似等于 1000 / 60 = 16.666毫秒
如果有多个requestAnimationFrame一起执行会怎样?
function test1(timeStamp) {
console.log("requestAnimationFrame1 ----", timeStamp);
requestAnimationFrame(test1);
}
function test2(timeStamp) {
console.log("requestAnimationFrame2 ----", timeStamp);
requestAnimationFrame(test2);
}
requestAnimationFrame(test1);
requestAnimationFrame(test2);
可以看到,两个 requestAnimationFrame
在控制台输出的时间戳是一样的。也就是浏览器刷新一次的时候,执行所有的 requestAnimationFrame
,并且它们的回调参数是一模一样的。
浏览器优化
为了提高性能和电池寿命,因此在大多数浏览器里,当
requestAnimationFrame()
运行在后台标签页或者隐藏的<iframe> 里时,requestAnimationFrame()
会被暂停调用以提升性能和电池寿命。
即当你切换到其他页面时, requestAnimationFrame()
会被暂停调用
返回值
每执行一次,数值就会 +1
var stratBtn = document.querySelector("#start");
var rAF;
stratBtn.addEventListener("click", () => {
rAF = requestAnimationFrame(test);
});
function test() {
rAF = requestAnimationFrame(test);
console.log("rAF:", rAF);
}
取消执行
传这个值给 window.cancelAnimationFrame() 以取消回调函数
var stratBtn = document.querySelector("#start"),
stopBtn = document.querySelector("#stop");
var rAF;
stratBtn.addEventListener("click", () => {
rAF = requestAnimationFrame(test);
});
stopBtn.addEventListener("click", () => {
cancelAnimationFrame(rAF);
});
function test() {
rAF = requestAnimationFrame(test);
console.log("rAF:", rAF);
}
应用
实现动画
<html>
<head>
<style>
#box {
width: 0;
height: 50px;
background-color: tomato;
}
</style>
</head>
<body>
<button id="start">开始</button>
<button id="stop">停止</button>
<div id="box"></div>
<script>
window.onload = function () {
var stratBtn = document.querySelector("#start"),
stopBtn = document.querySelector("#stop"),
box = document.querySelector("#box");
var rAF;
stratBtn.addEventListener("click", () => {
rAF = requestAnimationFrame(test)
});
stopBtn.addEventListener("click", () => {
cancelAnimationFrame(rAF);
});
function test() {
box.style.width = `${rAF}%`;
rAF = requestAnimationFrame(test)
}
}
</script>
</body>
</html>
requestAnimationFrame比起setTimeout、setInterval的优势主要有亮点:
1. requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率;
setTimeout、setInterval它们的内在运行机制决定了 时间间隔参数 实际上只是指定了把动画代码添加到 浏览器UI线程队列 中以等待执行的时间。如果队列前面已经加入了其它任务,那动画代码就要等前面的 任务完成后 再执行,并且如果时间间隔过短(小于16.7ms)会造成丢帧,所以就会导致动画可能不会按照预设的去执行,降低用户体验。
2. 在隐藏或不可见的元素中,将不会进行重新重绘或回流;
3. requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销
参考资料: