requestAnimationFrame
方法让咱们能够在下一帧开始时调用指定函数。可是不少人可能不知道,无论三七二十一直接在 requestAnimationFrame
的回调函数里绘制动画会有一个问题。是什么问题呢?要理解这个问题,咱们先要了解 requestAnimationFrame
的一个知识点。javascript
这个知识点就是 requestAnimationFrame
无论理回调函数。这一点在 w3c 中明确说明了。java
Also note that multiple calls to requestAnimationFrame with the same callback (before callbacks are invoked and the list is cleared) will result in multiple entries being in the list with that same callback, and thus will result in that callback being invoked more than once for the animation frame.
— w3cgit
即在回调被执行前,屡次调用带有同一回调函数的 requestAnimationFrame
,会致使回调在同一帧中执行屡次。咱们能够经过一个简单的例子模拟在同一帧内屡次调用 requestAnimationFrame
的场景:github
const animation = timestamp => console.log('animation called at', timestamp) window.requestAnimationFrame(animation) window.requestAnimationFrame(animation) // animation called at 320.7559999991645 // animation called at 320.7559999991645
咱们用连续调用两次 requestAnimationFrame
模拟在同一帧中调用两次 requestAnimationFrame
。dom
例子中的 timestamp
是由 requestAnimationFrame
传给回调函数的,表示回调队列被触发的时间。由输出可知,animation
函数在同一帧内被执行了两次,即绘制了两次动画。然而在同一帧绘制两次动画很明显是多余的,至关于画了一幅画,而后再在这幅画上再画上一样的一幅画。函数
那么什么场景下,requestAnimationFrame
会在一帧内被屡次调用呢?熟悉事件的同窗应该立刻能想到 mousemove
, scroll
这类事件。动画
因此前面咱们提到的问题就是:由于 requestAnimationFrame
无论理回调函数,在滚动、触摸这类高触发频率的事件回调里,若是调用 requestAnimationFrame
而后绘制动画,可能会形成多余的计算和绘制。例如:code
window.addEventListener('scroll', e => { window.requestAnimationFrame(timestamp => { animation(timestamp) }) })
在上面代码中,scroll
事件可能在一帧内屡次触发,因此 animation
函数可能会在一帧内重复绘制,形成没必要要的计算和渲染。队列
对于这种高频发事件,通常的解决方法是使用节流函数。可是在这里使用节流函数并不能完美解决问题。由于节流函数是经过时间管理队列的,而 requestAnimationFrame
的触发时间是不固定的,在高刷新频率的显示屏上时间会小于 16.67ms,页面若是被推入后台,时间可能大于 16.67ms。事件
完美的解决方案是经过 requestAnimationFrame
来管理队列,其思路就是保证 requestAnimationFrame
的队列里,一样的回调函数只有一个。示意代码以下:
const onScroll = e => { if (scheduledAnimationFrame) { return } scheduledAnimationFrame = true window.requestAnimationFrame(timestamp => { scheduledAnimationFrame = false animation(timestamp) }) } window.addEventListener('scroll', onScroll)
可是每次都要写这么一堆代码,也有点麻烦。因此我开源了 raf-plus 库用于解决这个问题,有须要的的同窗能够用用~
requestAnimationFrame
无论理回调函数队列,而滚动、触摸这类高触发频率事件的回调可能会在同一帧内触发屡次。因此正确使用 requestAnimationFrame
的姿式是,在同一帧内可能调用屡次 requestAnimationFrame
时,要管理回调函数,防止重复绘制动画。