Using requestAnimationFrame for smoother animations

Loading

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:

  1. Efficient Rendering: Syncs animations with the browser’s refresh rate (usually 60FPS).
  2. CPU Optimization: Stops executing when the tab is inactive, saving CPU usage.
  3. Better Performance: Reduces janky or choppy animations by avoiding redundant reflows and repaints.
  4. 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:

  1. A <div> with id="box" is created and styled.
  2. 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.

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

  1. Use event listeners to trigger animations
    • Instead of running animations continuously, start them only when needed.
  2. 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
  3. 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.

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 or setTimeout

By implementing best practices, you can create high-performance, visually appealing animations that work seamlessly across devices.


Additional Resources

Leave a Reply

Your email address will not be published. Required fields are marked *