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"));
});