A few years back, I saw a great episode of Mythbusters, where they tested the idea of the
Chinese water torture.
One by one, Adam, Kari, Scotty and Tory were each firmly secured before
being subjected to the slow, rhythmic beat of droplets onto their
foreheads.
And one by one, each was broken down — in Kari’s case, to the point of tears — in under an hour.
It’s not the most comfortable thing to watch, but it offers an obvious take-away: While some rhythms
can be soothing, tight repetition can quickly drive us stark-raving mad.
Of course, as web people, we already know this intuitively!
Who hasn’t experienced the site with the endlessly spinning logo? Who
hasn’t been driven to distraction by that industrious little
construction worker toiling away behind an “Under Construction” sign?
It raises an obvious question: Is it possible to use animation without introducing these mind-torturing loops?
Let’s see what we can do.
CSS3 Animation Basics
If you haven’t used CSS3 animations yet, here’s a crash course. If
you’ve already got some familiarity with CSS3 keyframes, jump to
“Working with CSS3 Sprites.”
To keep things breezy to read, I’m going to use the official
“unprefixed” W3C syntax (i.e. @keyframe) throughout this article, but
you’ll need to use the browser-specific prefixes (i.e.
@-webkit-keyframe, @-moz-keyframe, @-ms-keyframe, etc.) to see it
working in real-world browsers.
The core concept behind CSS3 animation is to let you transition
between CSS properties. Like all animation, CSS3 needs a timeline with a
start and a finish — set at 0% and 100% respectively. The code for the
simplest of animation timelines would look like this:
1 | @keyframes mysimpleanimation { |
2 | 0% { background-color : #f00 ;} |
3 | 100% { background-color : #00f ;} |
As you might guess, the above animation is designed to change a red
background to blue. However, by itself, this code won’t do anything
until we apply it to an HTML object on screen. We also need to set the
time length of the animation (in seconds).
Here’s how to include a time length within your CSS:
3 | animation: mysimpleanimation 4 s infinite; |
Here’s that simple example in action.
We’re free to create as many points along the timeline as we’d
like — for instance we can change our animation to green (#0f0) at the
halfway point by adding the following line:
1 | @keyframes mysimpleanimation { |
2 | 0% { background-color : #f00 } |
3 | 50% { background-color : #0f0 } |
4 | 100% { background-color : #00f } |
There are a slew of other well-documented attributes and settings, but we’ve covered the fundamentals you’ll need for now.
Working with CSS3 Sprites
Note: For all the demos in this article, I’m going to be using
Lea Verou’s super-lovely Dabblet webapp.
Apart from looking luscious and being hard-wired straight into
Github, Dabblet will let you fiddle with the CSS here and see the
results in real-time. The other great feature of Dabblet is that it
let’s you write plain, unprefixed CSS3 while magically adding the
browser-specific code in the background (using Lea’s “
–prefix-free”).
This is brilliant for keeping the code examples simple to read, but
be aware that you’ll need to add the “-moz,” “-webkit,” and “-ms”
prefixes to make this code work outside of Dabblet.
So, despite only really having one good trick — transitioning between
CSS properties along a timeline — CSS3 keyframes give you many of the
basic building blocks of animation including:
- Squash and stretch (in CSS: width, height)
- Rotation (transform:rotate)
- Position in space (left, right, top & bottom)
- Color and opacity changes (color, background-color, opacity)
Traditional cel animation (or perhaps the first Bugs report? #dadjoke)
This covers a lot of potential animation scenarios, from bouncing balls to spinning tires to launching rockets.
However, it’s not so obvious how you would, for instance, manipulate CSS properties to make:
- a wriggling snake
- flapping wings
- a spinning earth
- a running man
Classic cel (or sprite) animation
is one common answer to that problem. We’ve probably all seen the old
Disney or Warner Brothers hand-drawn “cartoon cels.” Each moment in
the timeline is drawn as a standalone image. Then each cel is displayed
rapidly in succession to give the illusion of movement.
So, how do we do that with CSS3?
Let’s begin with a simple cel animation that we can develop into
something more sophisticated. I’m starting with single .png showing a
hand-drawn flame over three frames. It won’t be hard to make this
image loop seamlessly.
Our 3 frame flame cel
Each flame cel is 136px high by 100px wide.
The Setup
Here’s the basic HTML framework that I’m starting with:
I’m using a container
(#logfire) to hold the background
graphic and a placeholder
(#flamegroup) that will hold our
flames in position. Inside the placeholder I’ve nested a third
(.flame) that will become our flame.
I’ve embedded the flame graphic into the background-image, but
we’re cropping off most of the image, only allowing the left-hand
flame to show.
4 | background : url (flame.png) left top no-repeat } |
The basic static scene
Here’s our starting code.
Setting Up Our Timeline
To make a seamless loop, we’ll need to create a 4-frame loop — that
is, we’ll show frames 1,2,3, and 2 again, and repeat. We could
represent that sequence along our invisible timeline as positions 0%,
25%, 50%, 75% and 100% (i.e. back to frame 1). At each stage we
reposition our background at the next frame, so our CSS might look
something like this:
3 | background-position : 0px 0px ; |
6 | background-position : -100px 0px ; |
9 | background-position : -200px 0px ; |
12 | background-position : -100px 0px ; |
15 | background-position : 0px 0px ; |
Then, we could attach our “flicker” animation to our flame class and set it to loop every 1.1 seconds:
3 | animation: flicker 1.1 s infinite; |
Fantastic!
Except for the part where it doesn’t look quite right.
As the above example shows, it animates
too
nicely, gracefully sliding between frames. Just like those little
page-flick animations you used to make in the corners of book pages,
the magic lies in snapping each new frame into place so quickly that you
can’t see the in-between state.
Our new jagged timeline now looks like this:
3 | background-position : 0px 0px ; |
6 | background-position : 0px 0px ; |
9 | background-position : -100px 0px ; |
12 | background-position : -100px 0px ; |
15 | background-position : -200px 0px ; |
18 | background-position : -200px 0px ; |
21 | background-position : -100px 0px ; |
24 | background-position : -100px 0px ; |
As you can see, our first cell stays motionless from 0%-25%. Then
suddenly at 25.1% along the timeline, we crank the background 100 pixels
to left. Again we leave it there till the 50% mark before cranking it
left again at 51.1%.
I think you get the idea.
If we plug that CSS into our example we now get a pleasantly wiggling flame.
Add a Pulse
Flames don’t just move, they vary mildly in intensity. Since we
already have the timeline set up, why not tweak the opacity a little to
give it a subtle pulse?
3 | background-position : 0px 0px ; |
7 | background-position : 0px 0px ; |
10 | background-position : -100px 0px ; |
13 | background-position : -100px 0px ; |
17 | background-position : -200px 0px ; |
20 | background-position : -200px 0px ; |
24 | background-position : -100px 0px ; |
27 | background-position : -100px 0px ; |
Here’s the result.
A decent result, but let’s face it: We could get something pretty
close to this with a good animated .gif file. Let’s ratchet things up a
little.
Fan those Flames
Circling back to our original HTML, we’re going to add more flame elements — lucky we have that handy holder
, huh?
Let’s add two more
elements with the “flame” class to our
“flamegroup.” We’ll also give each
it’s own ID so we can
target them separately.
We now have three flame elements, but since they’re dancing in
perfect unison, they still look like a single flame. Let’s delete the
animation from the “flame” class and add it to our new IDs.
2 | -webkit-animation: flicker . 7 s infinite; |
5 | -webkit-animation: flicker . 7 s infinite; |
8 | -webkit-animation: flicker . 7 s infinite; |
Now we are free to call the same animation (i.e. “flicker”), but
we’ll give each one a different timing to keep things interesting.
Riffing on the
prime number idea that we used in the cicada principle article, I’m going to set the flame elements to loop in 0.7, 1.1 and 1.3 seconds respectively (yes, I know they’re not primes).
Finally, rather than stacking them precisely on top of each other,
I’m going to offset each flame slightly (10-20px) from its siblings.
This is mainly so we can see the edges mingle and interact.
Here’s the CSS:
2 | animation: flicker 1.3 s infinite; |
7 | animation: flicker . 7 s infinite; |
12 | animation: flicker 1.1 s infinite; |
And here’s the final demo in action.
A screenshot — although the static image kinda defeats the purpose.
Our flames jump, dance and weave in a soft, less “GIF-loopy”
manner. Sometimes you think you have the pattern nailed but then it
seems to shift. Dabblet lets you tinker, so feel free to play with the
timing. Obviously the slower the loop, the longer between exact pattern
repeats.
There are a few points to be aware of:
We’ve also seen some evidence of our processor cycles spiking during
*some* larger CSS3 animations. It seems to be linked to particular types
of animation, but I haven’t isolated the key problem areas yet. Suffice
to say, if you hear your CPU fan kick into overdrive, you’ve probably
hit on the same issue.
Summing Up…
I think this idea of weaving together animation timings has some
interesting potential, so watch this space in the coming weeks for some
more experiments. Subtle sunlight, water, and clouds effects come
to mind.
So, what do you think?
And if you have any ideas of your own, make a Dabblet and share them here.
UPDATE: March 1st, 2012
Reading up on the CSS animation spec, I’ve found there’s a better way
to control the sprite switching. The @keyframe syntax has a reasonably
rarely-used attribute called ‘step’ that allows you to control how
many ‘inbetween’ animation states are generated by the browser. It looks
like this in it’s unprefixed state:
1 | animation: animation_name 1.1 s steps( 3 ) infinite; |
This code would generate three stages in-between the steps that
we’ve manually set out in our @keyframe code.When using sprites, we
don’t want show ANYTHING between changing sprites so we just set our
steps to 1.
1 | animation: flicker 1.1 s steps( 1 ) infinite; |
This allows us to greatly simplify our flame timing loop.
Here’s an updated Dabblet showing it working.
No comments: