2020已经过去五分之四,你确定还不来了解一下JS的rAF?( 二 )


request 会把每一帧中的所有DOM操作集中起来 , 在一次重绘或回流中就完成(这点很像虚拟DOM不是~) , 并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,这样就不会出现过度渲染的问题 , 保证了流畅的需求以及浏览器的完美渲染 。
requestAnimationFrame的优点为什么不使用settimeout?setTimeout通过设定一个时间间隔来不断的更新屏幕图像 , 从而完成动图 。它的优点是可控性高 , 可以进行编码式的动画效果实现 。
setTimeout缺点:

  1. 「造成无用的函数运行开销:」
【2020已经过去五分之四,你确定还不来了解一下JS的rAF?】也就是过度绘制 , 同时因为更新图像的频率和屏幕的刷新重绘制步调不一致 , 会产生丢帧 , 在低性能的显示器动画看起来就会卡顿 。
  1. 「当网页标签或浏览器置于后台不可见时 , 仍然会执行 , 造成资源浪费」
  2. 「API本身达不到毫秒级的精确:」
如果使用 setTimeout或者setInterval 那么需要我们制定时间 假设给予 (1000/60)理论上就可以完成60帧速率的动画 。 所以事实是浏览器可以“强制规定时间间隔的下限(clamping th timeout interval)”,一般浏览器所允许的时间在5-10毫秒 , 也就是说即使你给了某个小于10的数 , 可能也要等待10毫秒 。
  1. 「浏览器不能完美执行:」
当动画使用10ms的settimeout绘制动画时 , 您将看到一个时序不匹配 , 如下所示 。
2020已经过去五分之四,你确定还不来了解一下JS的rAF?文章插图
我们的显示屏一般是「16.7ms(即60FPS)的显示频率」 , 上图的第一行代表大多数监视器上显示的「16.7ms显示频率」 , 上图的第二行代表「10ms的典型setTimeout」 。 由于在显示刷新间隔之前发生了另一个绘制请求 , 因此无法绘制每次的第三个绘制(红色箭头指示) 。 这种透支会导致动画断断续续 , 「因为每三帧都会丢失」 。 计时器分辨率的降低也会对电池寿命产生负面影响 , 并降低其他应用程序的性能 。
如果使用requestAnimationFrame可以解决setTimeout的丢帧问题 , 因为它使应用程序时通知(且仅当)的浏览器需要更新页面显示 , 渲染时间由系统处理 。 因此 , 应用程序与浏览器绘画间隔完全一致 , 并且仅使用适当数量的资源 。
requestAnimationFrame的好处相比于setTimeout的在固定时间后执行对应的动画函数 , requestAnimationFrame用于指示浏览器在下一次重新绘制屏幕图像时, 执行其提供的回调函数 。
  • 「使浏览器画面的重绘和回流与显示器的刷新频率同步」它能够保证我们的动画函数的每一次调用都对应着一次屏幕重绘 , 从而避免setTimeout通过时间定义动画频率 , 与屏幕刷新频率不一致导致的丢帧 。
  • 「节省系统资源 , 提高性能和视觉效果」在页面被置于后台或隐藏时 , 会自动的停止 , 不进行函数的执行 , 当页面激活时 , 会重新从上次停止的状态开始执行 , 因此在性能开销上也会相比setTimeout小很多 。
兼容问题目前的时间点上 , 几乎所有的浏览器现行版本都支持了requestAnimationFrame函数 。 但在一部分浏览器上还需要加上兼容性前缀 。下面这是比较全面的方法用来使requestAnimation兼容各浏览器:
(function() {var lastTime = 0;var vendors = ['webkit', 'moz']; // 浏览器前缀// 当window.requestAnimationFrame不存在时执行for循环 , 添加前缀for(var x = 0; x < vendors.length++x) {window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] ||window[vendors[x] + 'CancelRequestAnimationFrame'];}//当添加前缀后依旧不存在 , 则使用setTimeout替代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);};}}());