htmlhtml5-canvasspritegame-developmentgame-engine

Multiple frame animation in HTML canvas


I've been trying to find an answer for a while but most searches return results for animating shapes not frame animation. Given a canvas with a 2d context to which I draw sprites with drawImage, what's the most efficient way to create multiple frames for an animated image? I'm looking for something like animated gif but using canvas, where I have a number of frames (eg: 4) with a fixed rate (eg: 500 ms) and I paint different tiles on each frame.

I need this for a tileset engine I thought of prototyping: The canvas is static by default, to support animated tiles like water you'd need multiple frames. The most obvious solution feels like having multiple canvas elements and showing / hiding them in order, but I'd like to check if there's a more efficient way, in case canvas supports multiple layers or CSS might work.


Solution

  • If you want to animate multiple frames efficiently in an HTML5 , you don’t need multiple canvas elements. Instead, you can update a single canvas by drawing different frames at regular intervals.

    Efficient Approach: Using requestAnimationFrame + setInterval The best way to handle this is:

    1. Load your sprite sheet (or individual frames).
    2. Keep track of the current frame.
    3. Use setInterval to update the frame at a fixed rate.
    4. Use requestAnimationFrame to continuously render the updated frame.

    Here’s an example:

    <canvas id="gameCanvas" width="200" height="200"></canvas>
    <script>
       const canvas = document.getElementById("gameCanvas");
       const ctx = canvas.getContext("2d");
       
       const spriteSheet = new Image();
       spriteSheet.src = "sprite.png"; // Your sprite sheet containing all frames
       
       const frameWidth = 64; // Width of a single frame
       const frameHeight = 64; // Height of a single frame
       const totalFrames = 4; // Total frames in the animation
       const frameRate = 500; // Change frame every 500ms
       
       let currentFrame = 0;
       
       spriteSheet.onload = () => {
           setInterval(() => {
               currentFrame = (currentFrame + 1) % totalFrames;
           }, frameRate);
       
           animate();
       };
       
       function animate() {
           ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear previous frame
       
           ctx.drawImage(
               spriteSheet,
               currentFrame * frameWidth, 0, frameWidth, frameHeight, // Source from sprite sheet
               0, 0, frameWidth, frameHeight // Draw onto the canvas
           );
       
           requestAnimationFrame(animate);
       }
    </script>
    

    Why This Works Well

    If you’re working on a tile-based engine, you can store animation states for different tiles and only update the ones that need it.