详解requestAnimationFrame

前言

这两天在学 canvas,刚好看到有地方使用了 requestAnimationFrame 这个方法,其实之前我也见过这个东西,与动画渲染有关,但没深入了解。既然再次碰到了,就认真学习一下用法。

早期定时动画

在没有这个 api 之前,js是如何制作动画的呢?答案是使用 setTimeout 和 setInterval 这两个定时器,相信大家也不陌生。但了解过事件循环的应该知道,这两个定时器并不能保证时间精度,他们只是在规定的时间后将回调函数添加到任务队列中,并不能保证回调按时执行。

并且,为了使得动画可以流畅展示,对于定时的要求也比较严格。一般 60 帧我们看起来就是比较舒服的,那么重绘的时间间隔应该是 1000毫秒/60,约为 17 毫秒。也就是在时间精度极高的情况下,重绘间隔为 17 毫秒才能让动画看起来平滑。

requestAnimationFrame

除了重绘间隔的要求,浏览器的时间精度也是很重要的,但由于各个浏览器的时间精度不一并且精度不能满足动画平滑渲染的要求,并且浏览器还对切换到后台和不活跃的签页中定时器执行限流,所以才有了 requestAnimationFrame。

Mozilla 的 Robert O’Callahan 提出了一个方案,创造一个名为 mozrequestAnimationFrame 的新方法,用来通知浏览器某些 JavaScript 代码要执行动画了,这样浏览器就可以在运行某些代码后进行适当的优化,随着这个方案的发展,现在已经能在各个浏览器使用没有前缀的版本了,也就是 requestAnimationFrame。

requestAnimationFrame 接收一个参数,这个参数是一个要在重绘屏幕之前调用的函数。这个函数就是修改 DOM 样式以反映下一次重绘有什么变化的地方。

下面是一个展示进度条的例子:

<body>
  <div id="process" style="width: 1px; height: 5px; background-color: black;"></div>
  <script>
    var a = 1
    var process = document.getElementById('process')
    function updateProcess() {
      console.log(new Date().getMilliseconds())
      if(a <= 100) {
        process.style.width = a + 'px'
        a++
        requestAnimationFrame(updateProcess)
      }
    }
    requestAnimationFrame(updateProcess)
  </script>
</body>

运行的动画就不看了,很平滑,我们主要来看打印的日志:
请添加图片描述

我们可以发现大约每 7 毫秒打印一次,也就是动画执行了一次。那这里为什么是 7 呢?其实和显示器的刷新频率有关,下图是我屏幕的刷新频率:
请添加图片描述

大概是 144hz,那么 1000 / 144 等于 6.94444…,约等于 7。看到这里就明白了,这个 requestAnimationFrame 还真精准,动画的平滑程度再不依赖于定时器的延时了,而是通过屏幕的刷新率决定的。

到这里,requestAnimationFrame 已经解决了浏览器不知道 js 动画什么时候开始以及最佳时间间隔是多少的问题了,但还有一个问题没解决,就是不知道自己的代码何时实际执行。其实 requestAnimationFrame 也给出了解决方案。

传递给 requestAnimationFrame 的函数实际上可以接收一个参数,此参数是一个 DOMHighResTimeStamp 的实例,表示下次重绘的时间。如果给出了精确的时间值,那么开发者也可以更好决定如何调优了。

function updateProcess() {
  // console.log(new Date().getMilliseconds())
  console.log(arguments) // 打印接收的参数
  if(a <= 20) {
    process.style.width = a + 'px'
    a++
    requestAnimationFrame(updateProcess)
  }
}

结果如下,可以看到间隔都是 7 毫秒左右:
请添加图片描述

cancelAnimationFrame

和 setTimeout 以及 setInterval 一样,requestAnimationFrame 也会返回一个 id,我们可以基于此 id 来取消重绘任务,这里就不展示了,用法和 clearTimeout 一样。

通过 requestAnimationFrame 节流

之前我们知道可以通过 setTimeout 来节流,以免大量执行大量重复的代码。但如果这些代码中包含修改 dom 样式的代码,我们想把事件处理程序的调用限制在每次重绘之前,可以通过 requestAnimationFrame 设置。

例如比较常见的滚动监听,我们一般都使用 setTimeout,在此基础上我们把 requestAnimationFrame 塞进来。通过这种方法,setTimeout 可以限制回调的执行次数,而 requestAnimationFrame 可以控制在浏览器的哪个渲染周期中去执行。

let enabled = true

function operation() {
  console.log(Date.now())
}

window.addEventListener('scroll', () => {
  if(enabled) {
    enabled = false
    window.requestAnimationFrame(operation)
    window.setTimeout(() => enabled = true, 50) // 限制 50 ms执行一次。
  }
})

现在看来,vue 中的 nextTick 这个钩子应该是用到了这个方法(待我有空去考察一下),之前面试都没扯到这里,只说到了事件循环。

本次学习 requestAnimationFrame 就到这里了,希望有帮助到你。

有任何问题欢迎评论和我交流哦~

参考:《JavaScript高级程序设计》(第4版)

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: `requestAnimationFrame` 是一个浏览器 API,它可以让你的 JavaScript 在下一个浏览器重绘之前运行。这样可以有效地提高动画的性能,因为它可以让浏览器在重绘之前知道你的动画需要做什么。使用`requestAnimationFrame`可以避免浏览器的重绘和回流,提高动画的流畅度。 使用方法: ``` // 定义动画函数 function animate() { // 在这里编写动画代码 requestAnimationFrame(animate); } // 开始动画 requestAnimationFrame(animate); ``` 注意:不同浏览器有不同的实现,如果不支持`requestAnimationFrame`,可以使用`setTimeout`或`setInterval`来模拟。 ### 回答2: requestAnimationFrame 是用来代替 setTimeout 或 setInterval 的一种优化方案,它可以让Javascript在浏览器的下一次重绘之前执行某个函数,以达到最佳的渲染效果。 它的优点在于可以让浏览器在渲染性能的限制下自动调整,以便保持每秒渲染帧率在60FPS左右,从而有效避免掉帧现象,一定程度上优化体验。 使用 requestAnimationFrame 的语法如下: window.requestAnimationFrame(callback); 其中,callback 是在下一帧渲染时要执行的函数,它会自动传入一些参数,例如时间戳等。 具体来说,requestAnimationFrame 的工作原理是这样的: 1. 当 Javascript 代码执行到 window.requestAnimationFrame() 时,会把回调函数放到一个队列里面等待执行。 2. 浏览器在下一次重绘之前会检查这个队列,如果队列里有函数,就执行这些函数,否则什么也不做。 3. 如果函数执行的过程中或执行完成后,将再次请求下一帧的重绘。 有了 requestAnimationFrame,我们就可以编写更加流畅自然的动画效果,而不必担心因为性能瓶颈导致掉帧,从而达到更好的用户体验。同时,它也可以有效避免由于 setInterval 或 setTimeout 过于频繁地操作Dom节点而引起的性能问题。不过值得注意的是,requestAnimationFrame 并不支持 IE9 及以下版本的浏览器,因此在进行兼容性考虑时需要引入 polyfill 或使用其他方案。 ### 回答3: requestAnimationFrame(简称RAF)是一种用于浏览器动画效果的方法。该方法通过以比setInterval更优秀的方式在浏览器下一次渲染时刻之前更新动画效果,以使动画流畅自然。在一些高要求的场景下,使用setTimeout和setInterval往往会导致更新不及时;而使用requestAnimationFrame可以保证效果在下一次渲染时完成,确保页面更新及时、不丢帧。 requestAnimationFrame的主要特点是: - 操作DOM次数减少,增强性能 - 优化页面动画,避免出现卡顿现象 - 适用于各种各样的动画效果 - 减少了原来程序需要处理时间,使得程序变得更加高效 requestAnimationFrame最大的优点是:在页面未找到其他的任务时,一次帧效果可以达到每秒60帧,基本上和显示器的刷新频率相匹配。此外,requestAnimaitonFrame会自动适应浏览器窗口大小的变化,并且可以在各种设备上正常工作。另外,requestAnimationFrame还可以在浏览器最小化、后台挂起等情况下暂停,以节省计算资源。 使用requestAnimationFrame的步骤如下: 1.调用requestAnimationFrame函数,并带有一个函数作为参数 2.在函数中改变元素属性或是Canvas的状态和图像/文本内容 3.使浏览器执行函数 使用requestAnimationFrame的方法一般都遵循“递归调用”的设计模式。就是说每次执行函数时都会加入下一个临界点循环,递归执行直到某个条件终止。 总之,requestAnimationFrame是一种比setTimeout更优秀的动画效果控制方法,其优点是高效、流畅,对于大多数的动画效果是一个很好的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值