动画requestAnimationFrame

前言

在研究canvas的2D pixi.js库的时候,其动画的刷新都用requestAnimationFrame替代了setTimeout 或 setInterval

但是jQuery中还是采用了setInterval,我这章就研究下顺便改造下jQuery的动画

 

定时器

jQuery动画的实现考虑到兼容与易用性采用了setInterval来不断绘制新的属性值,从而达到动画的效果。

大部分浏览器的显示频率是16.7ms,由于浏览器的特性,setInterval会有一个丢帧的问题

即使向其传递毫秒为单位的参数,它们也不能达到ms的准确性。这是因为javascript是单线程的,可能会发生阻塞

jQuery会有一个全局设置jQuery.fx.interval = 13 设置动画每秒运行帧数。

默认是13毫秒。该属性值越小,在速度较快的浏览器中(例如,Chrome),动画执行的越流畅,但是会影响程序的性能并且占用更多的 CPU 资源

那么归纳一点最关键的问题:

开发着并不知道下一刻绘制动画的最佳时机是什么时候

 

requestAnimationFrame

requestAnimationFrame 是专门为实现高性能的帧动画而设计的一个API

说简单点

  • setInterval、setTimeout是开发者主动要求浏览器去绘制,但是由于种种问题,浏览器可能会漏掉部分命令
  • requestAnimationFrame 就是浏览器什么要开始绘制了浏览器自己知道,通过requestAnimationFrame 告诉开发者,这样就不会出现重复绘制丢失的问题了

目前已在多个浏览器得到了支持,包括IE10+,Firefox,Chrome,Safari,Opera等,在移动设备上,ios6以上版本以及IE mobile 10以上也支持requestAnimationFrame,

唯一比较遗憾的是目前安卓上的原生浏览器并不支持requestAnimationFrame,不过对requestAnimationFrame的支持应该是大势所趋了,安卓版本的chrome 16+也是支持requestAnimationFrame的。

 

比较

区别:

  1. requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧
  2. 隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。
  3. requestAnimationFrame也会像setTimeout一样有一个返回值ID用于取消,可以把它作为参数传入cancelAnimationFrame函数来取消requestAnimationFrame的回调

 

兼容处理

摘自HTML5 Canvas 核心技术

window.requestNextAnimationFrame = (function () {
  var originalWebkitRequestAnimationFrame = undefined,
      wrapper = undefined,
      callback = undefined,
      geckoVersion = 0,
      userAgent = navigator.userAgent,
      index = 0,
      self = this;

  // Workaround for Chrome 10 bug where Chrome
  // does not pass the time to the animation function

  if (window.webkitRequestAnimationFrame) {
    // Define the wrapper

    wrapper = function (time) {
      if (time === undefined) {
        time = +new Date();
      }
      self.callback(time);
    };

    // Make the switch

    originalWebkitRequestAnimationFrame = window.webkitRequestAnimationFrame;

    window.webkitRequestAnimationFrame = function (callback, element) {
      self.callback = callback;

      // Browser calls the wrapper and wrapper calls the callback

      originalWebkitRequestAnimationFrame(wrapper, element);
    }
  }

  // Workaround for Gecko 2.0, which has a bug in
  // mozRequestAnimationFrame() that restricts animations
  // to 30-40 fps.

  if (window.mozRequestAnimationFrame) {
    // Check the Gecko version. Gecko is used by browsers
    // other than Firefox. Gecko 2.0 corresponds to
    // Firefox 4.0.

    index = userAgent.indexOf('rv:');

    if (userAgent.indexOf('Gecko') != -1) {
      geckoVersion = userAgent.substr(index + 3, 3);

      if (geckoVersion === '2.0') {
        // Forces the return statement to fall through
        // to the setTimeout() function.

        window.mozRequestAnimationFrame = undefined;
      }
    }
  }

  return  window.requestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.oRequestAnimationFrame ||
      window.msRequestAnimationFrame ||

      function (callback, element) {
        var start,
            finish;

        window.setTimeout(function () {
          start = +new Date();
          callback(start);
          finish = +new Date();

          self.timeout = 1000 / 60 - (finish - start);

        }, self.timeout);
      };
}());

  window.cancelNextRequestAnimationFrame = window.cancelRequestAnimationFrame
    || window.webkitCancelAnimationFrame
    || window.webkitCancelRequestAnimationFrame
    || window.mozCancelRequestAnimationFrame
    || window.oCancelRequestAnimationFrame
    || window.msCancelRequestAnimationFrame
    || clearTimeout;

 

对比

通过requestAnimationFrame与setInterval同样的效果对比

一段动画采用setInterval与requestAnimationFrame,分别给出了webKit下调用刷新的次数

setInterval实现,调用了144次左右

<!doctype html><img id="book" style="background:red;opacity:1;position: relative; left: 500px;" alt="" width="100" height="123" data-mce-style="background: red; opacity: 1; position: relative; left: 500px;" /><div id="several"><br /></div><script type="text/javascript"> var book = document.getElementById('book') var several = document.getElementById('several'); animate(book, { left: 50, duration: 2000 }) function animate(elem, options){ //动画初始值 var start = 500 //动画结束值 var end = options.left //动画id var timerId; var createTime = function(){ return (+new Date) } //动画开始时间 var startTime = createTime(); var i = 0; function tick(){ i++; several.innerHTML = 'setInterval调用次数:' + i; //每次变化的时间 var remaining = Math.max(0, startTime + options.duration - createTime()) var temp = remaining / options.duration || 0; var percent = 1 - temp; var stop = function(){ //停止动画 clearInterval(timerId); timerId = null; } var setStyle = function(value){ elem.style['left'] = value + 'px' } //移动的距离 var now = (end - start) * percent + start; if(percent === 1){ setStyle(now) stop(); }else{ setStyle(now) } } //开始执行动画 var timerId = setInterval(tick, 13); } </script>

 

requestAnimationFrame 实现调用了120次左右

<!doctype html><img id="book" style="background:red;opacity:1;position: relative; left: 500px;" alt="" width="100" height="123" data-mce-style="background: red; opacity: 1; position: relative; left: 500px;" /><div id="several"><br /></div><script type="text/javascript"> var book = document.getElementById('book') var several = document.getElementById('several'); animate(book, { left: 50, duration: 2000 }) function animate(elem, options){ //动画初始值 var start = 500 //动画结束值 var end = options.left var createTime = function(){ return (+new Date) } //动画开始时间 var startTime = createTime(); var timerId; //开始动画 var startAnim = function() { timerId = requestAnimationFrame(tick,15); } //停止动画 var stopAnim = function() { cancelAnimationFrame(timerId) } var i = 0; function tick(){ i++; several.innerHTML = 'requestAnimationFrame调用次数:' + i; //每次变化的时间 var remaining = Math.max(0, startTime + options.duration - createTime()) var temp = remaining / options.duration || 0; var percent = 1 - temp; var setStyle = function(value){ elem.style['left'] = value + 'px' } //移动的距离 var now = (end - start) * percent + start; if(percent === 1){ setStyle(now) stopAnim() ; }else{ setStyle(now) startAnim(tick); } } //开始执行动画 startAnim(tick); } </script>

 

通过对比,在同样的时间里,通过定时器刷新的次数是要更多,当然定时器跟设置的时间是有关系的

这里我要强调一点,有效值,就是我们在浏览器能接受的范围内,给出一个最佳的渲染时间,这样才是一个性能优化的最佳的选择

 

转载于:https://www.cnblogs.com/aaronjs/p/4283109.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值