Using requestAnimationFrame for JavaScript animation on the Web
It took me time to grasp the concept behind the requestAnimationFrame method. So I will briefly explain how to use requestAnimationFrame in web animations. Before then, let us understand how this method came to be and its advantages/uses.
What is animation?
In simple terms, animation is a way of bringing still objects to life. Web animation can serve purposes like;
creating professional websites,
keeping users engaged etc.
When properly applied, animations can build a strong connection between the users and the contents of a website. Several techniques and tools can animate web content, but I’ll focus on using JavaScript’s requestAnimationFrame.
Why requestAnimationFrame?
In the past, setInterval
was the only method for creating timed loops or animation in JavaScript. This traditional method involves repeatedly invoking a function 60 times per second to achieve smooth animation. However, the inconsistencies and unnecessary repaints and reflows surrounding this method were costly to the browser and central processing unit (CPU).
In 2011, Paul Irish introduced the requestAnimationFrame as a better alternative for web animation. Unlike setInterval, requestAnimationFrame allows the browser to decide the delay in repaint and reflow rate, optimizing and creating a smoother animation using a consistent frame rate. The requestAnimationFrame also pauses animation or redraws less in hidden or inactive browser tabs and during CPU overload. Web animations using requestAnimationFrame improve performance and battery life. We have mentioned repaint and reflow, but what do they have to do with the browser?
What are repaint and reflow?
Repaint involves calculating changes in the visual properties of an element in the Document Object Model (DOM). These changes include the following;
background,
visibility,
color etc.
Reflow involves calculating changes in the layout of an element in the DOM. These include the following;
shape,
size,
position etc.
Repaint and reflow can be expensive operations if done a lot. This is because they force the browser to validate or check the visibility and positions of other DOM nodes. This causes the user interface to lag and reduce or upset the user experience.
What is requestAnimationFrame?
The requestAnimationFrame is a JavaScript method that tells the browser to call a specified function and update an animation before the next repaint cycle. The syntax involves passing the callback function to requestAnimationFrame as an argument, which calls the callback function before the next repaint. You can check out MDN to learn more about requestAnimationFrame.
Syntax:
const nextRepaint = () => {
// Do something
requestAnimationFrame(nextRepaint)
}
requestAnimationFrame(nextRepaint)
The callback receives an argument, a DOMHighResTimeStamp, the current time in milliseconds since the page load. The performance.now()
is used as an alternative if the callback function receives no argument. The requestAnimationFrame method provides a long integer as the request-id. The window.cancelAnimationFrame()
method takes this request-id to cancel the animation request.
How to use requestAnimationFrame
To illustrate how requestAnimationFrame works, let’s create an animation to move a ball.
function animate() {
let runAgain = performance.now() + 100;
let step = 0;
function nextFrame(timestamp) {
if(runAgain <= timestamp) {
step += 5;
moveBall(step)
runAgain = timestamp + 100;
}
requestAnimationFrame(nextFrame);
}
requestAnimationFrame(nextFrame);
}
animate()
function moveBall(step) {
const ball = document.querySelector('.ball');
ball.style.transform = `translateY(${step}px)`;
}
Let’s run through how the code works. When calling the animate function, the following happens;
declares and assigns the variables
runAgain
and step their respective values,requestAnimationFrame calls the
nextFrame
function to execute the function body,the
nextFrame
receives a parameter with the value of the DOMHighResTimeStamp,the ball moves down as 5 gets added to the step variable value when the value held by
runAgain
is less than the timestamp’s value,updates
runAgain
variable with the current timestamp plus 100,requestAnimationFrame method recursively calls the
nextFrame
to let the browser update the animation.
That's all. The complete code to the example above can be viewed on codepen
Before now, our ball continues downward without stopping. Let's use the cancelAnimationFrame
method to halt the ball when it reaches the bottom of its container.
function animate() {
const containerDOMRect = document.querySelector('.ball-container').getBoundingClientRect();
let runAgain = performance.now() + 100;
let requestId = 0;
let step = 0;
function nextFrame(timestamp) {
if(runAgain <= timestamp) {
step += 5;
moveBall(step)
runAgain = timestamp + 100;
}
requestId = requestAnimationFrame(nextFrame);
if(step >= containerDOMRect.height) {
cancelAnimationFrame(requestId)
}
}
requestAnimationFrame(nextFrame);
}
The additional codes above do the following;
gets the height of the ball container,
declares and assigns zero to the
requestId
variable,updates the
requestId
variable with the integer provided by requestAnimationFrame,checks if the updated
step
variable value is greater than or equal to the container’s height,cancels or stops the repaint cycle with the updated
requestId
value provided to the cancelAnimationFrame method.
That's it, folks. You can find the link to the complete code here.
Conclusion
What we have discussed thus far should help you create simple web animations with requestAnimationFrame. But if you are a curious developer, there are resources to create more complex animations. So dive deep, don't relax at the ocean's surface. I hope you found the article helpful. Thanks for reading.
Further reading
https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
https://css-tricks.com/using-requestanimationframe/
Subscribe to my newsletter
Read articles from Chukwuemeka Timothy Ofili directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by