什么是requestAnimationFrame

x33g5p2x  于2022-03-05 转载在 其他  
字(2.3k)|赞(0)|评价(0)|浏览(240)

一、写在前面
随着前端的发展,css已经可以实现很多的动画效果,但是仍然存在很多的动画效果是css无法实现的,比如说页面滚动,通常的解决方案都是我们通过js的setInterval来设置定时器完成动画特效,如下所示。

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

上面代码实现的功能是每隔100ms都是重新执行回调函数来完成动画效果,然而,使用定时器可能会不可靠的。
二、js中定时器存在的问题
我们使用定时器时,其内部原理是将其放入另一个进程中等待相应的时间,然后再从进程中取出放入任务队列中等待执行,但是存在一个问题,如果等待时间结束后,任务队列中的任务并不为空。此时定时器就会出现误差。详情可以查看微任务和宏任务相关知识

这里的for循环为主程序任务会在定时器之前完成,但是当主程序完成后,时间大概是400ms,时间是远远大于50ms的,所以此时setTimeOut定时器存在问题。

三、requestAnimationFrame api
css来绘制动画效果的好处是浏览器可以计算每一帧的间隔时间,这样用户在看到页面上的动画时,就会感觉非常流畅。但是js实现的动画效果就不同了。也就是如上所示的问题,此时我们引入一个requestAnimation api来帮助我们解决该问题。其作用就是让浏览器流畅的执行动画效果。可以将其理解为专门为实现动画效果的api,通过这个api,可以告诉浏览器某一个js代码要执行动画,浏览器收到通知后,就会运行这些代码的时候进行优化,实现流畅的效果,而不再需要开发人员心烦刷新频率的问题。
使用方法如下:

<!DOCTYPE html>
<html lang="cn">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #box {
      width: 110px;
      height: 40px;
      background: red;
    }
  </style>
</head>
<body>
  <div id="box"></div>
  <script>
    const element = document.getElementById('box');
    function step(timestamp) {
      element.style.width = element.offsetWidth + 1 + 'px'
      if(element.offsetWidth < 1200) {
        requestAnimationFrame(step)
      }
    }
    window.requestAnimationFrame(step);
  </script>
</body>
</html>

requestAnimationFrame接收一个动画执行函数作为参数,并且在函数内部,当满足某一个条件时,进行递归,从而保证动画的流畅展示。
四、兼容性设置

如上图所示该api存在一些兼容性问题。我们可以设置一些polyfill

(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);
        };
    }
}());

如果我们需要在一些老版本的浏览器中使用,则可以加上上述代码。

相关文章