requestAnimationFrame for smooth animations
You can use requestAnimationFrame
for animations as an alternative to setTimeout
or setInterval
.
Instead of manually calling a function to draw a frame every x
milliseconds (setTimeout
or setInterval
),
requestAnimationFrame
gets called before its next repaint which could potentially vary in time between calls.
Since the function doesn't call itself on a loop like setInterval
, you will need to manually call
it again to continue drawing the next frame.
const animate = (timestamp) => {// draw somethingrequestAnimationFrame(animate)}requestAnimationFrame(animate)
The callback function which is given a timestamp, which should be used to calculate the next frame.
According to MDN,
The number of callbacks is usually 60 times per second, but will generally match the display refresh rate in most web browsers as per W3C recommendation.
and
Be sure to always use the first argument (or some other method for getting the current time) to calculate how much the animation will progress in a frame, otherwise the animation will run faster on high refresh rate screens.
Example
Let's say I wanted to animate a spinning box. A naïve way of implementing it with requestAnimationFrame
would be something
like this:
The problem with this approach is that the animation isn't guaranteed to run at the same speed on
everyone's screens. Someone with a 60Hz refresh rate would see one full rotation after 360 / 60
seconds.
Another way of thinking about it is: 1 degree rotation every function call, where the function gets
called 60 timies per second. Someone with a 120Hz refresh rate would see one full rotation
after 360 / 120
seconds, which would be twice as fast as someone with the 60Hz refresh rate.
A better approach to achieve consistant animations would be to figure out at what rate the animation
should run and use the timestamp
argument to figure out exactly what to draw on the screen.
d = r * t
In order to achieve one rotation in 360 / 60
seconds:
d = (360 / 60) * t
Now, on every function call, we'll use timestamp
to calculate how much time has passed
since the last invocation to figure out the correct degrees to rotate.
With this approach, if the function gets called twice as fast on one machine vs another, it will
still finish one rotation in 360 / 60
seconds.
Note that this example uses milliseconds for the rate because timestamp
is a number in milliseconds.
If you are viewing this on a 60Hz screen, both animations may look the same but the underlying
logic for setting the values are different.