Bootstrap

requestAnimationFrame

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

;