The requestAnimationFrame method lets us call the specified function at the start of the next frame. But what many people may not know is that there is a problem with animating directly in the requestAnimationFrame callback anyway. What’s the problem? To understand this, we need to understand one thing about requestAnimationFrame.
RequestAnimationFrame does not manage callback functions
The point is that requestAnimationFrame does not manage callback functions. This is explicitly stated in the W3C.
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. — W3C
That is, calling requestAnimationFrame with the same callback function multiple times before the callback is executed causes the callback to be executed multiple times in the same frame. We can simulate a scenario where requestAnimationFrame is called multiple times in the same frame with a simple example:
const animation = timestamp= > console.log('animation called at', timestamp)
window.requestAnimationFrame(animation)
window.requestAnimationFrame(animation)
// animation called at 320.7559999991645
// animation called at 320.7559999991645Copy the code
We simulate calling requestAnimationFrame twice in the same frame by calling requestAnimationFrame twice in a row.
In this example, timestamp is passed to the callback function by requestAnimationFrame and represents the time when the callback queue was triggered. From the output, we can see that the animation function is executed twice in the same frame, that is, animation is drawn twice. However, drawing an animation twice in the same frame is obviously redundant, the equivalent of drawing a picture and then painting the same picture over the same picture.
The problem
In what scenario will requestAnimationFrame be called multiple times within a frame? Those of you who are familiar with events should immediately think of events like Mousemove and Scroll.
So the problem we mentioned earlier is that since requestAnimationFrame does not manage callback functions, calling requestAnimationFrame and then drawing an animation in a high-trigger event callback like scrolling or touch might cause extra computation and drawing. Such as:
window.addEventListener('scroll', e => {
window.requestAnimationFrame(timestamp= > {
animation(timestamp)
})
})Copy the code
In the above code, the Scroll event might fire multiple times within a frame, so the animation function might draw repeatedly within a frame, causing unnecessary calculations and rendering.
The solution
The common solution for such high frequency events is to use throttling functions. But using throttling functions here is not a perfect solution. Because the throttling function is run through a time management queue, requestAnimationFrame’s trigger time is not fixed and will be less than 16.67ms on a high-refresh display, or more than 16.67ms if the page is pushed into the background.
The perfect solution is to manage the queue with requestAnimationFrame. The idea is to ensure that there is only one callback function in the queue of requestAnimationFrame. The schematic code is as follows:
const onScroll = e= > {
if (scheduledAnimationFrame) { return }
scheduledAnimationFrame = true
window.requestAnimationFrame(timestamp= > {
scheduledAnimationFrame = false
animation(timestamp)
})
}
window.addEventListener('scroll', onScroll)Copy the code
But it’s a little bit of a hassle to write a bunch of code every time. So I open source raf-Plus library to solve this problem, students who need to use ~
conclusion
RequestAnimationFrame does not manage a queue of callback functions, and callbacks to events with high trigger frequency, such as scrolling and touch, may be triggered multiple times within the same frame. So the proper posture for using requestAnimationFrame is to manage callbacks to prevent repeated drawing of the animation when requestAnimationFrame may be called multiple times within the same frame.