Sprite animation example

by sfoster

HTML

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
  <meta charset="utf-8">
  <title>Sprite ("Film strip") animation</title>
</head>
<body>
  <p>In this example, the animation is laid out like a film strip, which frames stacked vertically</p>
  <p>I'm using a image here, but you could probably adapt to use a CSS backgroud-image. We indicate the number of frames in a data-count attribute. The final size of the animating image is given by the container we put it in. In this case 128x128 px defined in CSS.</p>
  <p>The animageImage function take a HTML image argument, gets the frame count from the data-count attribute (which gets mapped for us in the `dataset.count` property, and figures out the frame dimensions from there. The loop advances the animation frame every `fts` milliseconds, by moving the image up the correct number of pixels.</p>
  <p>I made it loop 4 times, but that could be infinite or whatever.</p>
  <div class="anim-outer" id="example1">
    <img class="anim-inner" src="https://www.codeandweb.com/static/5a10e3c3c1a2579a347e50573382d713/283d3/spritestrip.png" data-count="6">
  </div>
</body>
</html>

CSS

.anim-outer {
      width: 128px;
      height: 128px;
      overflow: hidden;
      position: relative;
      display: inline-block
    }
    .anim-outer > img {
      position: absolute;
      top: 0; left: 0;
      /* force-scale the image to fit its container */
      height: 100%;
      transform: translateX(0px);
    }

JavaScript

function animateImage(img) {
      let frameIndex = 0;
      let frameCount = parseInt(img.dataset.count);
      let frameSize = img.getBoundingClientRect().width / frameCount; 
      let animating = true;
      let lastFrameTime = Date.now();
      let loopCount = 0;
      const fps = 1000/3;
      console.log(frameSize, frameCount, );


      function advance() {
        const now = Date.now();
        if (!animating) {
          return;
        }
        if (now - lastFrameTime >= fps) {
          img.style.transform = `translateX(${-frameIndex * frameSize}px)`;
          frameIndex += 1;
          lastFrameTime = Date.now();
          if (frameIndex >= frameCount -1) {
            frameIndex = 0;
            loopCount++;
          }
          if (loopCount > 4) {
            animating = false
          }

        }
        requestAnimationFrame(advance);
      }
      advance();
    }
    window.addEventListener("load", () => {
      animateImage(document.querySelector("#example1  > img"));
    });