js中requestAnimationFrame

257 阅读2分钟

js中requestAnimationFrame

为什么要说它,源于在牛客中的一个选择题。我之前还从未接触过requestAnimationFrame,很是无语,所以去百度了一番,结果里面还真有大学问。

for(let i = 0; i < 5; i++){
    requestAnimationFrame(() => console.log(i));
}

这里有个前提知识,就是计算机刷新频率一般是60HZ,所以换算一下大概16.7ms刷新一次

requestAnimationFrame是一个类似于setTimeout的接口,主要用途就是将DOM操作集中起来,在一帧(16.7ms)时间点去集中完成。从而提升流畅度,改善视觉效果。


结合一个例子去简单阐述一下,正好看到有位博主在写一个js中的无限循环动画,感觉用这个例子可以很清晰的描述出来。

使用setInterval来实现无限循环

<!doctype html>
<html lang="en">
<head>
    <title>Document</title>
    <style>
        #e{
            width: 100px;
            height: 100px;
            background: red;
            position: absolute;
            left: 0;
        }
    </style>
</head>
<body>
<div id="e"></div>

<script>
    let target = document.getElementById("e");
    let flag = true;
    let left = 0;

    function render() {
        if(flag == true){
            if(left>=100){
                flag = false
            }
            target.style.left = ` ${left++}px`
        }else{
            if(left<=0){
                flag = true
            }
            target.style.left = ` ${left--}px`
        }
    }
    setInterval(function(){
         render()
    },1000/60)

</script>
</body>
</html>

normal.gif

使用requestAnimationFrame实现无限循环

<!doctype html>
<html lang="en">
<head>
    <title>Document</title>
    <style>
        #e{
            width: 100px;
            height: 100px;
            background: red;
            position: absolute;
            left: 0;
        }
    </style>
</head>
<body>
<div id="e"></div>
<script>


    let target = document.getElementById("e");
    let flag = true;
    let left = 0;

    function render() {
        if(flag == true){
            if(left>=100){
                flag = false
            }
            target.style.left = ` ${left++}px`
        }else{
            if(left<=0){
                flag = true
            }
            target.style.left = ` ${left--}px`
        }
    }

    //requestAnimationFrame效果
    (function animloop() {
        render();
        window.requestAnimationFrame(animloop);
    })();

</script>
</body>
</html>

requestAnimationFrame.gif

呃呃呃~原本想展现一下使用requestAnimationFrame很流畅的样子,但是录制出来的gif效果好像不怎么明显。 大家可以自行实现一番,看看效果。

流畅原因

这里说明一下为什么使用requestAnimationFrame之后就会很流畅,感觉这里也是一个重要的知识点。

  • 如果用setInterval,则会在16.7ms之后进行执行(准确来说是放入异步队列),只有当主线程任务执行完后才会执行队列中的任务,因此实际执行时间总是比设定时间要晚。所以它并不是在16.7ms内执行了一次,可能会发生在16.7ms内执行多次,从而发生多次重排重绘事件,因此看上去就很卡顿。
  • 如果使用requestAnimationFrame,则明确就是说在16.7ms内执行一次,并且在这段时间内完成重排重绘事件,所以就流程许多

回归正题

requestAnimationFrame虽然是异步函数,但是由于i是用let定义的,每一次循环都会生成一个块级作用域,保存当前的值。

如果换成var i = 0,则输出结果为 4 4 4 4