用js实现一个无限循环的动画。requestAnimationFrame详解

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

首先想到的是定时器

<!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帧。

既然setInterval可以搞定为啥还要用requestAnimationFrame呢?最直观的感觉就是,添加api的人是个大神级牛人,我只能怀疑自己。

所以搜索相关问题发现以下两点

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

1、requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。

2、在隐藏或不可见的元素中,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>

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

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

1、怎么停止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>

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

2、如果我想动画频率降低怎么做,为什么不考虑加快呵呵 当前刷新频率已经是屏幕的刷新频率了再快也没有意义了

这个略微麻烦点

默认情况下,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>

 

到此结束了。欢迎吐槽! 

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: `requestAnimationFrame` 是一个浏览器 API,它可以让你的 JavaScript 在下一个浏览器重绘之前运行。这样可以有效地提高动画的性能,因为它可以让浏览器在重绘之前知道你的动画需要做什么。使用`requestAnimationFrame`可以避免浏览器的重绘和回流,提高动画的流畅度。 使用方法: ``` // 定义动画函数 function animate() { // 在这里编写动画代码 requestAnimationFrame(animate); } // 开始动画 requestAnimationFrame(animate); ``` 注意:不同浏览器有不同的实现,如果不支持`requestAnimationFrame`,可以使用`setTimeout`或`setInterval`来模拟。 ### 回答2: requestAnimationFrame 是用来代替 setTimeout 或 setInterval 的一种优化方案,它可以让Javascript在浏览器的下一次重绘之前执行某个函数,以达到最佳的渲染效果。 它的优点在于可以让浏览器在渲染性能的限制下自动调整,以便保持每秒渲染率在60FPS左右,从而有效避免掉现象,一定程度上优化体验。 使用 requestAnimationFrame 的语法如下: window.requestAnimationFrame(callback); 其中,callback 是在下一渲染时要执行的函数,它会自动传入一些参数,例如时间戳等。 具体来说,requestAnimationFrame 的工作原理是这样的: 1. 当 Javascript 代码执行到 window.requestAnimationFrame() 时,会把回调函数放到一个队列里面等待执行。 2. 浏览器在下一次重绘之前会检查这个队列,如果队列里有函数,就执行这些函数,否则什么也不做。 3. 如果函数执行的过程中或执行完成后,将再次请求下一的重绘。 有了 requestAnimationFrame,我们就可以编写更加流畅自然的动画效果,而不必担心因为性能瓶颈导致掉,从而达到更好的用户体验。同时,它也可以有效避免由于 setInterval 或 setTimeout 过于频繁地操作Dom节点而引起的性能问题。不过值得注意的是,requestAnimationFrame 并不支持 IE9 及以下版本的浏览器,因此在进行兼容性考虑时需要引入 polyfill 或使用其他方案。 ### 回答3: requestAnimationFrame(简称RAF)是一种用于浏览器动画效果的方法。该方法通过以比setInterval更优秀的方式在浏览器下一次渲染时刻之前更新动画效果,以使动画流畅自然。在一些高要求的场景下,使用setTimeout和setInterval往往会导致更新不及时;而使用requestAnimationFrame可以保证效果在下一次渲染时完成,确保页面更新及时、不丢requestAnimationFrame的主要特点是: - 操作DOM次数减少,增强性能 - 优化页面动画,避免出现卡顿现象 - 适用于各种各样的动画效果 - 减少了原来程序需要处理时间,使得程序变得更加高效 requestAnimationFrame最大的优点是:在页面未找到其他的任务时,一次效果可以达到每秒60,基本上和显示器的刷新频率相匹配。此外,requestAnimaitonFrame会自动适应浏览器窗口大小的变化,并且可以在各种设备上正常工作。另外,requestAnimationFrame还可以在浏览器最小化、后台挂起等情况下暂停,以节省计算资源。 使用requestAnimationFrame的步骤如下: 1.调用requestAnimationFrame函数,并带有一个函数作为参数 2.在函数中改变元素属性或是Canvas的状态和图像/文本内容 3.使浏览器执行函数 使用requestAnimationFrame的方法一般都遵循“递归调用”的设计模式。就是说每次执行函数时都会加入下一个临界点循环,递归执行直到某个条件终止。 总之,requestAnimationFrame是一种比setTimeout更优秀的动画效果控制方法,其优点是高效、流畅,对于大多数的动画效果一个很好的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值