HTML & CSS
Article

Responsive Sprite Animations with ImageMagick and GreenSock

By Tom Bennet

CSS Sprites are not new. Since being popularized on A List Apart in 2004, the humble sprite has become a staple technique in many a web developer’s toolkit. But while the speed benefits afforded by sprites are well-understood, their uses in modern web animation are rarely discussed. The principle remains the same: combine multiple images into a single ‘master’ graphic, of which only selected portions are displayed at a time.

In this article, we’ll explore an easy way to create responsive CSS sprite animations that are lightweight, mobile-friendly, and even interactive. You don’t need any special graphics software, just a bit of CSS and JavaScript know-how. Let’s get started.

Building A Sprite

Sprites originate from the world of video games, so we’ll pay tribute to their heritage and animate Ryu from Street Fighter. Needless to say, all artwork belongs to Capcom.

We need to combine each frame of our animation into a single image. There are dozens of tools out there to aid with the creation of sprites, many of which will even generate an accompanying stylesheet for you. Compass’s built-in spriting features are immensely powerful, and SpritePad is a good example of a web-based generator. For our purposes, however, a simple command-line approach works fine.

ImageMagick, the ‘swiss army knife’ of image processing, is a free and open-source image manipulation tool that’s perfect for automating tasks that could become laborious, such as combining images. ImageMagick is also available for just about every operating system. Mac users can install it via Homebrew, Windows adherents can download an executable installer from the official website, and Linux devotees probably don’t need me to explain anything.

Save the identically-sized frames of your animation in a folder as a sequence of PNGs. Break out a command-line terminal, navigate (cd) to the directory where your images are saved, and execute the following command:

convert *.png -append result-sprite.png

This command instructs ImageMagick to vertically concatenate all the .PNG files in our directory (since ‘*’ is essentially a wildcard) into one complete sprite called “result-sprite.png”. This is the graphic that we will apply as a background-image in CSS to the element we want to animate, altering its position to cycle through each frame in sequence.

Pure CSS Animation

We’ll start simply, and animate our sprite with CSS3 keyframe animation. If you’re unfamiliar with the basic syntax, Louis Lazaris’s article on Smashing Magazine is a very good introduction. We’re looking to animate the vertical background-position of our element from 0% to 100% (top to bottom), exposing each frame in turn.

Allowing the animation to run in one continuous sequence, as per default behaviour, would expose our sprite’s in-between frames and ruin the illusion. By using the steps() function, however, we can control the number of rendered keyframes. Because of the way background-position works when defined as a percentage, we’ll need to set the number of steps to be one fewer than the total number of frames in our animation.

Experiment with the following CodePen to get the idea:

See the Pen MYRKmJ by SitePoint (@SitePoint) on CodePen.

Note that we’ve defined the width and height of our element to exactly match that of a single frame, to avoid any bleeding of prior or subsequent frames. We’ve used the shorthand notation to set the animation-iteration-count to “infinite” and the animation-duration to 3.5 seconds, with the result that Ryu will carry out his punch-kick-hadoken combo until the end of time.

This is far from perfect, however. Try changing the height of our Ryu element, or applying a background-size to our sprite, and the effect will break. Our animated element is not yet responsive, and is dependent on rigid, pixel-based dimensions.

Making it Responsive

If we wish to freely resize our animated sprite, we have two options. A key to both options is our sprite’s dependence on a consistent aspect ratio, rather than a particular size. We must retain its proportions for it to scale correctly.

The first option is to set the background-size of our sprite to 100% of the width of our element, convert the width and height of our element from pixels to ems, and then alter the base font-size as required. This would achieve the desired effect, and may be suitable for most purposes, but would not result in true fluidity.

Our second option is to take advantage of Marc Hinse’s pure-CSS fixed aspect ratio technique, which uses percentage-based padding on a pseudo-element to maintain an element’s proportions at any size. This trick is used in the demo below to give our sprite a fluid width of 70%. Responsive Ryu.

See the Pen zxXrzP by SitePoint (@SitePoint) on CodePen.

This is a lightweight and mobile-friendly method of achieving a responsive frame-by-frame animation effect. Browser support for CSS animations is excellent, with the CSS3 keyframe syntax being the only limiting factor. Include the appropriate -webkit- vendor prefix, and the effect works in all modern browsers including IE10 and above.

JavaScript Animation

What if we want more precise control over our animation than is generally possible with the CSS3 keyframe syntax? Let’s say that instead of a single, looping animation, we wish to incorporate multiple sprites into a complex scene and bind triggers and timing functions to the position of the scrollbar?

For this to be possible, we will need to animate our sprites with JavaScript, harnessing the power of the GreenSock animation platform using Jan Paepke’s excellent library ScrollMagic. For those unfamiliar with this powerful combination, the official ScrollMagic demos are a great place to start.

These are powerful tools on which entire series of tutorials have been written. As such, rather than diving into the details of how to work with ScrollMagic, we will instead demonstrate some of the powerful timing functions now available to us. The basic sprite principles remain identical to our earlier pure-CSS animation.

First, let’s trigger a 2-second-long animation at a particular scroll position. It should finish on the final frame, and play in reverse when the user scrolls back up (even if this occurs midway through playback). We’ll stick with the video game theme and use a sprite from Westwood Studios’ strategy classic Red Alert, now released as freeware.

See the Pen Demo 3: JavaScript-Powered Timing Functions by SitePoint (@SitePoint) on CodePen.

Note that we’re using the aforementioned em-based sizing method; try altering the font-size to scale the sprite up and down. We’ve defined a TweenMax.to tween, setting the destination value of our element’s vertical background-position to 100%, and used the SteppedEase method to achieve the required frame-by-frame effect.

Let’s take this one step further. We’ll synchronise our animation with the scroll position, using the scrollbar like a playback scrub control.

See the Pen Demo 4: Synchronising Playback with the Scrollbar by SitePoint (@SitePoint) on CodePen.

The sprite remains in a fixed position for the duration of the animation, which is now controlled by the user’s scroll behaviour. To achieve this effect, we’ve given our ScrollMagic.Scene a duration of 1500px and used the setPin method to fix the parent element for the entirety of the scene. For a more detailed breakdown of these techniques, be sure to peruse the excellent documentation on the ScrollMagic website.

Browser support for these JavaScript-powered sprite animations is even better than that of our pure-CSS version. ScrollMagic and GreenSock are supported in all modern browsers including IE 9+, with browser prefixes and inconsistencies handled automatically. Mobile support is also good; these techniques work on iOS 8+ with no special workarounds, including on-the-fly repainting and no loss in scroll momentum.

Conclusion

We’ve barely scratched the surface of what’s made possible by combining responsive CSS sprites with advanced scrolling animation, but it’s my hope to inspire others to start experimenting. I’d love to see any sites that use variations of the techniques described above. You can take a look at the personal project that first prompted me to explore sprite animation — videogame aficionados may notice a pattern.

Finally, in the words of Rachel Nabors, “please animate responsibly.” Just because something can be animated it doesn’t necessarily mean it should be. And if you do choose to animate, do so deliberately, beautifully, and with purpose.

Thanks for reading!

Comments
tanay1337

It was really an awesome article, Tom. I will definitely use this technique someday, thanks for teaching us smiley

tombennet

Thanks Tanay, I'm really pleased you found the technique useful!

I'd be very interested to see what you create. Happy spriting.

TWeber

Nice article!
i would like to use responsive sprites in a project at our company, but i have encountered a problem with rounding of the background-postition. I forked your pen (http://codepen.io/anon/pen/zGvOZV) and put in our testing sprites and like you can see, when the sprites container is not exactly the width of one frame, the entire frame moves up and down by one pixel. I was wondering if you had an Idea how this could be fixed.

Thanks in advance

tombennet

Hi TWeber,

Thanks for this, it’s an interesting problem.

I’ve dug into the cause, and I believe it’s related to the way you’ve scaled your sprite. Your original 10-frame sprite is 4000 pixels in height, making for a neat 400 pixels of vertical movement per frame. It is also an extremely precise image, requiring accuracy down to a single pixel to retain its composition (namely, the one-pixel border).

In your Pen, the image is scaled down to 3413 pixels in height, which equates to 341.3 pixels per frame. This is rounded up or down depending on the frame number, resulting in the slight positioning shift that you see, and made more noticeable by the one-pixel border flickering in and out of view.

So, I would say that this problem is caused by the inherent difficulty of retaining accuracy down to a single pixel in a responsive environment. As a workaround, I suppose you could include a small amount of whitespace around each frame (padding, essentially, and transparent if need be) to make the one-pixel shift less noticeable.

I hope this is helpful. If anyone else has suggestions for how to address this issue, I’d love to hear them!

Thanks again,

Tom

PaulOB

This is the problem with sprites when you need absolute pixel precision and you should never make a sprite where it exactly joins to another sprite either because when the page is zoomed the sprite will show artefacts from adjoining sprites as the image is rounded up or down. Always allow at least a 3 pixel gap between your sprites.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Front-end, once a week, for free.