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开销?
参考资料:
【今天你更博学了么】一个神奇的前端动画 API requestAnimationFrame - 掘金
window.requestAnimationFrame - Web API 接口参考 | MDN
|