Our last post about the new hardware accelerated spinner, which has all of the benefits and none of the drawbacks, went over really well with our internal teams, but we kept getting the same question: “We really love the fluidity of this, but what if we want to animate something more complex than this just as smoothly?” Fear not! We’ve got that technique covered for you here, too.

This time, we’ll discuss how to hardware-accelerate a sprite animation, which in our case will have translucency. First, we should start by saying what a “sprite” is. A sprite is a sequential arrangement of individual cels or frames of animation combined into one image file, like if you took all of the frames of animation that make up a cartoon and laid them all out next to each other. In traditional animation, a cel (which makes up a single frame of animation) is drawn on a sheet of transparency paper (historically, celluloid) so the page doesn’t occlude the background behind it. A stack of cels, when viewed one at a time in rapid succession, creates the “animation”. This metaphor translates well to what we’ll be doing today with our sprite PNG image file, since it, too, will have translucency, which will allow it to overlay cleanly on top of our background. Unlike traditional animation, our frames will all live together on one large image file (a sprite sheet), instead of a separate image file for each frame. To simulate animation between them, we, in rapid succession, slide the sprite sheet past a sort of picture frame that hides everything outside of it, so that only one cel is visible at a time, much like a film strip moving through a movie projector. This sliding is what we can hardware-accelerate.

Getting Started

We’ll first need a sprite-sheet to animate.

Everyone likes explosions, right? Here’s one from this article on sprite explosions.

You’ll notice that this sheet has each of the frames laid out in a horizontal approach, each progressive frame lives to the right of its predecessor, until the edge of the image, when it wraps around to the next “line” or row. When we shift this image around using our animation’s keyframes, we’ll need to remember and account for this vertical shifting as well as the horizontal one. Each of our sprites measures 93 pixels wide by 100 pixels tall, for a total dimension of 930x400. We’ll also need these measurements in our calculations later.

CSS Keyframes

If we were just sliding an element ( DIV tag) from point A to point B in a hardware accelerated way, it would look like the following (Note the usage of translateZ() , which tells the browser to render this as if it were a 3D transform, which will be hardware accelerated):

@keyframes slide { 0% { transform: translateZ(0) translateX(0); } 100% { transform: translateZ(0) translateX(-930px); } } .move-me { animation: 2s slide infinite linear; }

For sprite animation we really don’t want a smooth animation, we need a stepped animation, one that has several stop-points along the way, and jumps from one part to the next. For this, we can use the steps() animation timing function. This does just what we need, stepping the animation one section at a time, and if we put the number of frames per row into this function, it will do all our work for us, like this:

@keyframes slide { 0% { transform: translateZ(0) translateX(0); } 100% { transform: translateZ(0) translateX(-930px); } } .move-me { animation: 2s slide infinite steps(10); }

We would be practically done if we only had one row of frames, but we have four. We need to jump down to the start of the next line, quickly, then animate across, jump down to the next, etc. until we’re done. Putting two keyframe percentages which are almost the same in value next to each other causes a quick jump. Below is what we’d need for our four rows. It’s important to note that the steps() function only refers to the number of steps between any two keyframes, not the entire animation. So, in our case, that relates to the number of columns we have, not the total number of frames.

keyframes-tv slide { 0% { transform: translateZ(0) translateY(0) translateX(0); } 25% { transform: translateZ(0) translateY(0) translateX(-930px); } 25.01% { transform: translateZ(0) translateY(-100px) translateX(0); } 50% { transform: translateZ(0) translateY(-100px) translateX(-930px); } 50.01% { transform: translateZ(0) translateY(-200px) translateX(0); } 75% { transform: translateZ(0) translateY(-200px) translateX(-930px); } 75.01% { transform: translateZ(0) translateY(-300px) translateX(0); } 100% { transform: translateZ(0) translateY(-300px) translateX(-930px); } } .move-me { animation: 2s slide infinite steps(10); }

All of the positioning translate numbers we’ve been using up to now have been negative numbers. This is because our frames are typically laid out left-to-right, top-to-bottom; and since we measure from the top left corner, we must subtract each time we want our sheet to move left or up. Our goal is to get each frame’s top left corner to be in the top left corner of its containing element on every step of the animation, so the sprite sheet’s cels look like they are animating. We’ll also need to “clip” off the parts of the sprite sheet we don’t want to see at the moment. This is easier than it sounds: simply add a wrapper tag around your sprite-sheet tag, set its dimensions to the size of one frame, and set it to overflow: hidden . Here’s an example:

<div class="explosion frame"> <div class="sprites"></div> </div> /* Pretend we're using the keyframes from the previous example */ .explosion { height: 100px; width: 93px; overflow: hidden; } .explosion .sprites { animation: 2s slide infinite steps(10); }

Demo: And here’s an example of this in practice. Space ships added for dramatic effect.

Demo: See how it works in slow motion.

Enyo Sprites

In Enyo, we’ve made this much easier so you can focus on building cool stuff. We’ve added some helpers to more easily accomplish this for you. Most importantly, enyo.SpriteAnimation which lets you declare all of the necessary properties in one kind.

{kind: "enyo.SpriteAnimation", name: "sprite1", src: "http://www.polybeast.de/portfolio/SkybusterExplosion.jpg", width: 320, height: 240, rows: 5, columns: 4, duration: 2000},

Demo: See the enyo.SpriteAnimation class in action

This kind also allows for additional properties like iterationCount , which lets you specify how many times this animation should run; cellOrientation , which lets you change the orientation of your sprite, in case it’s column-centric instead of row-centric; and offsetTop and offsetLeft , which lets you specify how far from the top left corner of the image your sprite range actually starts – this is useful if you have several different sprite animations in one image file.

To support enyo.SpriteAnimation , we’ve also added a new kind ( enyo.Style ) and a new mixin ( enyo.StylesheetSupport ), which you may find useful on their own.

StylesheetSupport Mixin

The new enyo.StylesheetSupport mixin can be applied to any UiComponent control and will dynamically generate and insert a side-car stylesheet that lives next to the control’s node in the DOM. It’s associated with the control, and can be updated at any time by appending-to, replacing, or deleting the contents of the stylesheet. The mixin adds a stylesheetContent property and an addStylesheetContent() method to your control. The stylesheetContent is primarily how you’d set and reset the CSS in the stylesheet, while the addStylesheetContent() method lets you append additional styles to the end of the stylesheet without replacing the existing styles in it.

For some context, here’s how it’s used in the SpriteAnimation kind:

... components: [ {kind: "enyo.Image", name: "spriteImage", mixins: ["enyo.StylesheetSupport"], sizing: "cover", styles: "background-size: initial;"} ], updateKeyframes: function() { this.$.spriteImage.set("stylesheetContent", this._generateKeyframes()); }, _generateKeyframes: function() { return 'some really cool CSS returned as a string'; } ...

Style Kind

Our last new addition is enyo.Style, which is much simpler than the last two. Its job is just to accept CSS stylesheet code and render it into a <style> tag, wrapping the content with proper HTML comments. Simple, but something you may find useful.

We’re reviewing the enyo.Style kind for elaboration, too; adding new functions like insertRule and deleteRule with a supplied index so you can have a much finer grained control over your dynamic stylesheets. What do you think? Is this an awesome feature, something you’d use, something that’s utterly useless? What could you come up with to take advantage of it?

We’re looking forward to seeing what sorts of cool stuff you can come up with to build. Feel free to drop us a line via twitter @EnyoJS or in the comments to show off your creations! We love seeing your hard work (and ours) come to life!