资讯专栏INFORMATION COLUMN

你需要知道的requestAnimationFrame

fireflow / 2142人阅读

摘要:你需要知道的随着前端的发展,已经能够实现非常多的动画特效,但是仍然存在无法完成的动画任务比如页面滚动,通常的解决方案都是使用中的来设置定时器来实现动画特效,比如下面的一个基本的动画循环。

你需要知道的requestAnimationFrame

随着前端的发展,css已经能够实现非常多的动画特效,但是仍然存在css无法完成的动画任务(比如页面滚动),通常的解决方案都是使用js中的setInterval来设置定时器来实现动画特效,比如下面的一个基本的动画循环。

(function() {
  function updateAnimations() {
    doAnimation1();
    doAnimation2();
  }
  setInterval(updateAnimations, 100);
})();

该代码实现的功能是每隔100毫秒执行函数操作来达到动画效果,然而,使用计时器真的可靠吗?
答案当然是 no

由于JavaScript是单线程的,所以定时器的实现是在当前任务队列完成后再执行定时器的回调的,假如当前队列任务执行时间大于定时器设置的延迟时间,那么定时器就不是那么可靠了,如下所示:

let startTime = new Date().getTime();
setTimeout(()=>{
  let endTime = new Date().getTime();
  console.log(endTime - startTime);
},50)

for(let i=0;i<20000;i++) {
  console.log(1);
}

输出如下

可以看到,设置了50毫秒后执行,实际执行延迟时间远大于这个数值,这就会导致动画效果并不会达到想要的效果。

动画是由浏览器按照一定的频率一帧一帧的绘制的,由css实现的动画的优势就是浏览器知道动画的开始及每一帧的循环间隔,能够在恰当的时间刷新UI,给用户一种流畅的体验,而setInterval或setTimeout实现的JavaScript动画就没有这么可靠了,因为浏览器压根就无法保证每一帧渲染的时间间隔,一般情况下,每秒平均刷新次数能够达到60帧,就能够给人流畅的体验,即每过 1000/60 毫秒渲染新一帧即可,但从上面的例子知,这一点单靠定时器是无法保证的。
为此,requestAnimationFrame应运而生,其作用就是让浏览器流畅的执行动画效果。可以将其理解为专门用来实现动画效果的api,通过这个api,可以告诉浏览器某个JavaScript代码要执行动画,浏览器收到通知后,则会运行这些代码的时候进行优化,实现流畅的效果,而不再需要开发人员烦心刷新频率的问题了。

使用方法如下:

function animationWidth() {
  var div = document.getElementById("box");
  div.style.width = parseInt(div.style.width) + 1 + "px";

  if(parseInt(div.style.width) < 200) {
    requestAnimationFrame(animationWidth)
  }
}
requestAnimationFrame(animationWidth);

效果如下(GIF录制的有点卡。。。实际效果请参考示例):

可以看到,requestAnimationFrame接受一个动画执行函数作为参数,这个函数的作用是仅执行一帧动画的渲染,并根据条件判断是否结束,如果动画没有结束,则继续调用requestAnimationFrame并将自身作为参数传入。从示例来看,得到了效果平滑流畅的动画,这样就巧妙地避开了每一帧动画渲染的时间间隔问题。

requestAnimationFrame的兼容性参考caniuse

在高级浏览器中,开发人员不用去操心每一帧动画渲染的时间间隔问题,而针对低版本浏览器,则需要使用setTimeout来模拟requestAnimationFrame,且针对不同浏览器对requestAnimationFrame的实现,这个api的名字也略有差异,针对低版本浏览器的模拟requestAnimationFrame的写法如下(来自张鑫旭大神的博客):

(function() {
    var lastTime = 0;
    var vendors = ["webkit", "moz"];
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"];
        window.cancelAnimationFrame = window[vendors[x] + "CancelAnimationFrame"] ||    // Webkit中此取消方法的名字变了
                                      window[vendors[x] + "CancelRequestAnimationFrame"];
    }

    if (!window.requestAnimationFrame) {
        window.requestAnimationFrame = function(callback, element) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
            var id = window.setTimeout(function() {
                callback(currTime + timeToCall);
            }, timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };
    }
    if (!window.cancelAnimationFrame) {
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
    }
}());

具体含义不做解释,如果遇到低版本浏览器的动画需求,你只需要把这段代码丢进去定义一个低配版requestAnimationFrame方法即可(珍爱生命,远离老浏览器啊)。

最后,再贴个部门工作中关于帧动画的示例
帅气的动画效果

参考:

JavaScript高级程序设计

CSS3动画那么强,requestAnimationFrame还有毛线用?

如有错误,欢迎指正!

我的github

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107482.html

相关文章

  • requestAnimationFrame 方法用对了吗?

    摘要:即在回调被执行前,多次调用带有同一回调函数的,会导致回调在同一帧中执行多次。例子中的是由传给回调函数的,表示回调队列被触发的时间。完美的解决方案是通过来管理队列,其思路就是保证的队列里,同样的回调函数只有一个。 requestAnimationFrame 方法让我们可以在下一帧开始时调用指定函数。但是很多人可能不知道,不管三七二十一直接在 requestAnimationFrame 的...

    miya 评论0 收藏0
  • requestAnimationFrame 方法用对了吗?

    摘要:即在回调被执行前,多次调用带有同一回调函数的,会导致回调在同一帧中执行多次。例子中的是由传给回调函数的,表示回调队列被触发的时间。完美的解决方案是通过来管理队列,其思路就是保证的队列里,同样的回调函数只有一个。 requestAnimationFrame 方法让我们可以在下一帧开始时调用指定函数。但是很多人可能不知道,不管三七二十一直接在 requestAnimationFrame 的...

    frontoldman 评论0 收藏0
  • requestAnimationFrame 方法用对了吗?

    摘要:即在回调被执行前,多次调用带有同一回调函数的,会导致回调在同一帧中执行多次。例子中的是由传给回调函数的,表示回调队列被触发的时间。完美的解决方案是通过来管理队列,其思路就是保证的队列里,同样的回调函数只有一个。 requestAnimationFrame 方法让我们可以在下一帧开始时调用指定函数。但是很多人可能不知道,不管三七二十一直接在 requestAnimationFrame 的...

    defcon 评论0 收藏0
  • requestAnimationFrame 方法用对了吗?

    摘要:即在回调被执行前,多次调用带有同一回调函数的,会导致回调在同一帧中执行多次。例子中的是由传给回调函数的,表示回调队列被触发的时间。完美的解决方案是通过来管理队列,其思路就是保证的队列里,同样的回调函数只有一个。 requestAnimationFrame 方法让我们可以在下一帧开始时调用指定函数。但是很多人可能不知道,不管三七二十一直接在 requestAnimationFrame 的...

    Taonce 评论0 收藏0
  • requestAnimationFrame 方法用对了吗?

    摘要:即在回调被执行前,多次调用带有同一回调函数的,会导致回调在同一帧中执行多次。例子中的是由传给回调函数的,表示回调队列被触发的时间。完美的解决方案是通过来管理队列,其思路就是保证的队列里,同样的回调函数只有一个。 requestAnimationFrame 方法让我们可以在下一帧开始时调用指定函数。但是很多人可能不知道,不管三七二十一直接在 requestAnimationFrame 的...

    syoya 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<