六个requestAnimationFrame的用法

4,063 阅读3分钟



如果你简历说你精通个js动效,但事实又不了解requestAnimationFrame,,那么你的面试结果是很难看的。


实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout 来实现,css3 可以使用 transition 和 animation 来实现,html5 中的 canvas 也可以实现。除此之外,html5 还提供一个专门用于请求动画的API,那就是 requestAnimationFrame,顾名思义就是请求动画帧。


requestAnimationFrame其用法跟setTimeout差不多,与setTimeout相比,最大的优势是由浏览器来决定函数的执行时机。形象一点的解释就是:告诉浏览器说 “我这里有一个函数要执行,你有空了帮忙执行一下”,然后浏览器相对比较空闲的时候就给执行了。


用法一:动画

requestAnimationFrame api本身的设计就是用来解决js动画的性能问题。那么,为什么requestAnimationFrame做动画性能会更好呢?主要原因在于requestAnimationFrame更加智能,它并非加快执行速度,而是适当时候降帧,防止并解决丢帧问题。当它发现无法维持60fps的频率时,它会把频率降低到30fps来保持帧数的稳定。也就是说如果上一次raf的回调执行时间过长,那么触发下一次raf回调的时间就会缩短,反之亦然,这也是为什么说由浏览器来决定执行时机性能会更好。

(function animloop(){
  window.requestAnimFrame(animloop);
  render();
})();

用法二: 函数节流

在高频率事件中,为了防止16ms内发生多次函数执行,使用raf可保证16ms内只触发一次,这既能保证流畅性也能更好的节省函数执行的开销。16ms内函数执行多次没有意义,因为显示器16ms刷新一次,多次执行并不会在界面上有任何显示。

举个例子:

var $box = $('#J_box'),
    $point = $box.find('i');
$box.on('mousemove',function(e){
  requestAnimationFrame(function(){
      $point.css({
          top : e.pageY,
          left : e.pageX
      })
  })
})

用法三:CPU节能

requestAnimationFrame的另一个特性是:如果页面不是激活状态下的话,函数会自动暂停,有效节省了CPU开销。在移动端,如果页面中有自动播放的轮播图、倒计时或使用setTimeout/setInterval来执行任务的定时器。那么当app进到后台或是锁屏后,webviewcorethread仍然持续占用CPU,导致耗电。而使用raf可以很简单的解决此类问题。

PS:复杂情况下,可配合window.onblur & window.focusdocument.onvisibilitychangedocument.onpause & document.onresume等相关的方法进行实现,但这篇文章里不做深入探讨。

(function(){
        var timer;
        var $txt = $('#J_num2'),
                num = 0;
        function play(){
            timer = setTimeout(function(){
                  //使用raf实现非激活状态下不运行
                requestAnimationFrame(function(){
                    stop();
                    next();
                });
            },1000)
        }
        function stop(){
            clearTimeout(timer)
        }
        function next(){
            $txt.text(num++);
            play();
        }
        play();
    })();

用法四:优雅降级

由于兼容性问题,需要降级对接口进行封装,优先使用高级特性,再根据浏览器不同情况进行回退,直到只能使用settimeout。参考[GitHub](github.com/darius/requ…

用法五:分帧初始化

都知道,requestAnimationFrame的执行时间约为16.7ms,即为一帧。那么可以使用它将页面初始化的函数进行打散到每一帧里,这样可以在初始化时降低CPU及内存开销

很多页面,初始化加载时,CPU都会有很明显的波动,就是因为大量的操作都集中到了一点上。

举个例子:

页面中有4个模块,A、B、C、D,在页面加载时进行实例化,一般的写法类似于:

$(function(){
    new A();
    new B();
    new C();
    new D();
})

而使用raf可将每个模块分别初始化,即每个模块都有16ms的初始化时间

$(function(){
        var lazyLoadList = [A,B,C,D];
        $.each(lazyloadList, function(index, module){
        window.requestAnimationFrame(function(){new module()});
        }); 
    })

用法六:异步化

requestAnimationFrame实际是一种异步化的操作,曾经setTimeout(function(){},0)一度成为解决了很多前端疑难杂症的法宝。而现在,可以用它来代替。


参考资料

[1]developer.mozilla.org/zh-CN/docs/…

[2]简书 糕糕AA www.jianshu.com/p/f6d933670…z