Using requestAnimationFrame
for Smoother Animations
Introduction
Animation is a crucial part of web development, enhancing the user experience by making interactions visually appealing and engaging. Traditional animation techniques using setInterval
or setTimeout
have drawbacks, such as performance issues, lack of synchronization with the browser’s rendering cycle, and unnecessary CPU usage.
The requestAnimationFrame
method provides a modern, efficient way to handle animations in JavaScript, synchronizing with the browser’s refresh rate to ensure smoother rendering.
What is requestAnimationFrame
?
requestAnimationFrame
is a built-in JavaScript method that optimizes animations by allowing the browser to control their execution. Instead of setting a fixed interval (like setInterval
), this method ensures that animations run at the best possible frame rate without dropping frames or causing unnecessary strain on the CPU.
Syntax:
window.requestAnimationFrame(callback);
Where callback
is a function that will be executed before the next repaint.
Key Benefits:
- Efficient Rendering: Syncs animations with the browser’s refresh rate (usually 60FPS).
- CPU Optimization: Stops executing when the tab is inactive, saving CPU usage.
- Better Performance: Reduces janky or choppy animations by avoiding redundant reflows and repaints.
- Smooth Frame Rate: Prevents frame skipping, making animations feel fluid.
How requestAnimationFrame
Works
Understanding the Browser’s Refresh Rate
Most modern displays refresh at 60Hz, meaning they refresh 60 times per second. That means we ideally want to update animations every 16.67ms (1000ms / 60 frames = ~16.67ms per frame).
requestAnimationFrame
schedules an update right before the browser repaints the screen, ensuring smooth, efficient animations.
Implementing Basic Animation with requestAnimationFrame
Let’s start with a simple animation example: moving a box across the screen.
Step 1: HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>requestAnimationFrame Animation</title>
<style>
#box {
width: 50px;
height: 50px;
background: red;
position: absolute;
top: 100px;
left: 0;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
let box = document.getElementById('box');
let position = 0;
function animate() {
position += 2;
box.style.left = position + 'px';
if (position < window.innerWidth - 50) {
requestAnimationFrame(animate);
}
}
animate();
</script>
</body>
</html>
Explanation:
- A
<div>
withid="box"
is created and styled. - The
animate()
function:- Moves the box by increasing the
left
position. - Calls
requestAnimationFrame(animate)
to update the position in sync with the browser’s refresh rate. - Stops when the box reaches the right edge.
- Moves the box by increasing the
Comparing requestAnimationFrame
vs. setInterval
Using setInterval
for animations:
setInterval(() => {
position += 2;
box.style.left = position + 'px';
}, 16);
Problems:
- Runs regardless of whether the browser is rendering.
- Can cause frame skipping if the animation takes longer than 16ms.
- Less efficient since it doesn’t pause when the tab is inactive.
Using requestAnimationFrame
:
function animate() {
position += 2;
box.style.left = position + 'px';
if (position < window.innerWidth - 50) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
Advantages:
- Syncs with the display refresh rate.
- Stops when the page is inactive.
- Prevents unnecessary CPU usage.
Handling Multiple Animations
You can use requestAnimationFrame
to run multiple animations simultaneously.
Example: Moving two elements independently:
<div id="box1" class="box"></div>
<div id="box2" class="box"></div>
<script>
let box1 = document.getElementById('box1');
let box2 = document.getElementById('box2');
let position1 = 0, position2 = 0;
function animateBox1() {
position1 += 2;
box1.style.left = position1 + 'px';
if (position1 < window.innerWidth - 50) {
requestAnimationFrame(animateBox1);
}
}
function animateBox2() {
position2 += 3; // Moves faster
box2.style.left = position2 + 'px';
if (position2 < window.innerWidth - 50) {
requestAnimationFrame(animateBox2);
}
}
animateBox1();
animateBox2();
</script>
Here, both box1
and box2
animate independently at different speeds.
Pausing and Resuming Animations
You can pause and resume animations by using cancelAnimationFrame()
.
Example:
let animationId;
let running = false;
function animate() {
position += 2;
box.style.left = position + 'px';
if (position < window.innerWidth - 50) {
animationId = requestAnimationFrame(animate);
}
}
document.getElementById('start').addEventListener('click', () => {
if (!running) {
running = true;
animate();
}
});
document.getElementById('stop').addEventListener('click', () => {
cancelAnimationFrame(animationId);
running = false;
});
Usage:
- Clicking “Start” resumes the animation.
- Clicking “Stop” pauses the animation.
Optimizing requestAnimationFrame
- Use event listeners to trigger animations
- Instead of running animations continuously, start them only when needed.
- Reduce layout recalculations
- Avoid unnecessary
style
updates inside the animation loop. - Example:
let left = box.offsetLeft; // Cache the value box.style.left = left + 2 + 'px';
Instead of:box.style.left = box.offsetLeft + 2 + 'px'; // Causes reflow
- Avoid unnecessary
- Batch DOM updates
- Instead of updating multiple elements one by one:
box1.style.left = pos1 + 'px'; box2.style.left = pos2 + 'px';
Use requestAnimationFrame to update all at once.
- Instead of updating multiple elements one by one:
requestAnimationFrame
is the best approach for animations in modern web development. It ensures:
- Smooth frame rates
- Efficient CPU usage
- Synchronization with the display refresh cycle
- Better performance over
setInterval
orsetTimeout
By implementing best practices, you can create high-performance, visually appealing animations that work seamlessly across devices.