requestAnimationFrame详解

为何要说它,源于看到的一道面试题:问题是用js实现一个无限循环的动画。html

首先想到的是定时器前端

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


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

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

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

能够说是完美实现!面试

至于时间间隔为何是1000/60,这是由于大多数屏幕渲染的时间间隔是每秒60帧。api

既然setInterval能够搞定为啥还要用requestAnimationFrame呢?最直观的感受就是,添加api的人是个大神级牛人,我只能怀疑本身。浏览器

因此搜索相关问题发现如下两点oop

requestAnimationFrame 比起 setTimeout、setInterval的优点主要有两点:

一、requestAnimationFrame 会把每一帧中的全部DOM操做集中起来,在一次重绘或回流中就完成,而且重绘或回流的时间间隔牢牢跟随浏览器的刷新频率,通常来讲,这个频率为每秒60帧。
二、在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这固然就意味着更少的的cpu,gpu和内存使用量。测试

直接上代码:动画

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


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

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

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

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

我没有添加各个浏览器的兼容写法,这里只说用法。spa

效果是实现了,不过我想到两个问题。3d

一、怎么中止requestAnimationFrame?是否有相似clearInterval这样的相似方法?

第一个问题:答案是肯定的 必须有:cancelAnimationFrame()接收一个参数 requestAnimationFrame默认返回一个id,cancelAnimationFrame只须要传入这个id就能够中止了。

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


    var e = document.getElementById("e");
    var flag = true;
    var left = 0;
    var rafId = null


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

    //requestAnimationFrame效果
    (function animloop(time) {
        console.log(time,Date.now())
        render();
        rafId = requestAnimationFrame(animloop);
        //若是left等于50 中止动画
        if(left == 50){
            cancelAnimationFrame(rafId)
        }
    })();

    //setInterval效果
    // setInterval(function(){
    //     render()
    // },1000/60)

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

2019-10-10 at 10.19.07.gif

附上一个效果图。也可直接capy代码测试。

二、若是我想动画频率下降怎么作,为何不考虑加快呵呵 当前刷新频率已是屏幕的刷新频率了再快也没有意义了

这个略微麻烦点

默认状况下,requestAnimationFrame执行频率是1000/60,大概是16ms多执一次。

若是咱们想每50ms执行一次怎么办呢?

requestAnimationFrame执行条件相似递归调用 (说的是相似)别咬我,既然这样的话咱们可否自定一个时间间隔再执行呢?固然定时器这么low的东西咱们就不考虑了,都已经抛弃它用rAF了(都快结束了我才想起写简写太他妈长了),
这个思路来源于我几年前搞IM的一个项目,服务端推送消息为了减少包的大小不给时间戳,这个咱们作前端的都知道,咱们虽然很牛逼 不过用户更牛逼,万一改了时间就很差玩了。

解决方案是 当和服务端通讯时 记录下一个时间差,(时间差等于服务端时间-本地时间)无论正负咱们只要这个时间差。这样每当咱们接受到消息 或者发送消息的时候咱们就拿本地时间和是价差相加。这样就能够保证和服务端时间是一致的了,思路是否是很牛逼哈哈。

撤了半天咱们经过以上思路来解决下rAF改变间隔的问题

上代码

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


    var e = document.getElementById("e");
    var flag = true;
    var left = 0;
    //当前执行时间
    var nowTime = 0;
    //记录每次动画执行结束的时间
    var lastTime = Date.now();
    //咱们本身定义的动画时间差值
    var diffTime = 40;

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

    //requestAnimationFrame效果
    (function animloop() {
        //记录当前时间
        nowTime = Date.now()
        // 当前时间-上次执行时间若是大于diffTime,那么执行动画,并更新上次执行时间
        if(nowTime-lastTime > diffTime){
            lastTime = nowTime
            render();
        }
        requestAnimationFrame(animloop);

    })()
</script>
</body>
</html>

附上一个效果:2019-10-10 at 10.58.30.gif

到此结束了。欢迎吐槽!

相关文章
相关标签/搜索