Earth Rotating Sprite Animation

I wanted to animate a sprite by targeting a changing data attribute + unique css background-positioning. I win.

The setup is pretty basic. I have an event listener on the html element for mousemove. Using that event I can see how far you moved your mouse (event.movementX). I add that number to an existing counter. Sprinkle on some math (below) and you have the current frame. Easy peasy.

The sprite that I'm using has 4 rows and 12 columns. So I had to add in the math for wrapping around from position 0, 11 to 1, 0. That's why I use $sprite-height and $frame-height below.



  • Width of sprite image: $sprite-width
  • Height of sprite image: $sprite-height
  • Number of horizontal frames: $x-max
  • Number of vertical frames: $y-max
  • Frame width: $frame-width = $sprite-width / $x-max
  • Frame height: $frame-height = $sprite-height / $y-max
#frame-container { width: round($frame-width); height: round($frame-height); background-image: url(/path/to/sprite.jpg); @for $frame from 0 to ($y-max * $x-max) { &[data-number="#{$frame}"] { $x-position: 1 - round(($frame % $x-max) * $frame-width); $y-position: 1 - round(floor($frame / $x-max) * $frame-height); background-position: #{$x-position} #{$y-position}; } } }


There's almost nothing going on here. Set an event listener on the html element for mousemove. In the caught event, find the horizontal movement using movementX. Add that to the existing frame number and add a large multiple of numberOfFrames to account for negative numbers (you don't want a negative frame number). Then, find the modulus of that number to get a final frame number between zero and the maximum number of frames. Finally, set that number to the container's data attribute, and let the CSS do the rendering work.

const frameContainer = document.getElementById('frame-container'); const numberOfFrames = 48; let number = 0; document.documentElement.addEventListener('mousemove', (event) => { number = ((number + event.movementX) + (numberOfFrames * 100)) % numberOfFrames; frameContainer.dataset.number = number; });