Create slick HTML5 animations
Modern browsers have several dedicated animation technologies built in. Martin Görner showcases the top four to try in your next project
Modern mobile operating systems have made animated user interfaces the standard of computer interaction – and have shown that fluid and well chosen animations create immersing effects and favour intuitive interactions. We all take for granted that you can set a list into motion with a flick of the finger and that it will keep going as if it had weight and inertia until it bumps against its end and bounces back a little. However, on the web we are not quite there yet.
In 2013, it’s time to sit back and relax. Modern browsers have several dedicated animation technologies built in, which run at 60 fps. Now is the time to forget about the past and start building UI experiences with tasteful animations that help the user feel at home. I would like to take you on a guided tour of the four animation technologies the modern web has to offer to help you decide which one is right for your project.
CSS3 animations – also in 3D
CSS can be used to declare animations using two basic properties: transition and animation. The transition property tells the browser to compute intermediate frames between two states, each defined by its own CSS. The animation is triggered when the CSS of the element changes. For example, because you programmatically changed its class or because a :hover CSS has kicked in.
The animation property is best used when you need a continuously running animation. It also lets you define intermediate steps in the animation.
To create a spinning image:
You can animate a surprisingly large number of CSS properties. Getting creative with an animated border-width is possible, if you feel so inclined. However, the most useful properties for animations are geometric transforms and opacity. CSS3 offers the full range of 2D geometric transforms through the handy transform property: translate, rotate, scale and skew.
To create 2D transforms with rotate, scale and skew:
And here is where things get interesting. If you feel that adding 3D to CSS, a technology designed for bolds and italics, is complete lunacy, keep reading. It’s been done in a very natural way by extending geometric transforms into the third dimension. The same transform property can now also do translateX, translateY, translateZ, as well as rotateX, rotateY, rotateZ.
To rotate an image in 3D:
With a little bit more effort, and an animation thrown in, look, a spinning 3D cube.
It wouldn’t be any fun if it really was this easy. The spec designers made sure to keep us entertained by leaving a couple of pitfalls on the road. If you try to rotate a single image, the image is rendered by default by the browser without perspective:
It can be added using the perspective property, which unfortunately defaults to an infinite value with the effect of no perspective at all. Objects close to the camera as well as far away are the same size.
To make things look realistic, you have to specify a finite value for the distance between the camera and the screen. Adding -webkitperspective: 1000px usually does the trick.
A bit of maths: what is perspective?
To represent a 3D object on a flat screen, you draw a line from the eye or camera to the point in 3D space you want to represent. Its intersection with the screen is where the corresponding pixel should be. In order to find the coordinates of the pixel on the screen, using the Thales’s theorem, you need the 3D coordinates of the point in space and the distance between the camera and the screen (f is also called the focal distance in optics). That is the distance provided through the perspective property. If that distance is infinite, you can see that all the rays (eye to 3D point lines) are horizontal and close and far away objects have the same size on the screen.
Now we think we have everything we need to set up a 3D cube, using six images and CSS 3D transforms, the next step is to make it spin.
That should be easy: enclose it in a div on which we apply a 3D rotation. If we do so, here is what happens.
The browser does exactly what you tell it to do: it renders a 3D cube in the div, then applies a 3D rotation on it as if it was a flat picture. That is not what you want. Unfortunately, it’s the default behaviour. To tell the browser that you want to compose the 3D transforms applied to nested divs, you have to use the -webkit-transform-style: preserve-3d property. With that, we get the spinning cube we wanted.
Getting it right
It’s easy to get lost in space. My recommendation is to start with a div that we will call the SCENE. That is where the perspective property goes. Inside, put a div called OBJECT. It should have the transform-style: preserve-3d property and this div is where you apply the transforms that will move the entire object. Finally, inside that div, use 3D transforms to place the faces as you wish to create the desired object – in our case, the six faces of the cube:
The great thing with 3D CSS transforms is that they work flawlessly with CSS animations and transitions. One exercise left is to animate a spinning cube and have it open up when the mouse cursor hovers over it. Quite easy: the transforms placing the faces of the cube place them at a certain distance from the centre of the cube. A second set of CSS properties with a :hover selector place them at a greater distance. By applying a transition: 1s property to the faces, you’ll see the cube open up on hover, even as it keeps spinning (see a demo).
Scalable Vector Graphics (SVG)
HTML and CSS are quite powerful as animation technologies but lack drawing primitives. This is what SVG is for and it comes with its own set of animation tags. The animation part of the SVG spec is called Synchronised Multimedia Integration Langugage (SMIL).
First, here is what SVG was invented for: vector primitives such as rectangles, circles and Bézier curves (shown in image 11):
One of those primitives, the path tag, is the Swiss army knife of SVG. It lets you define an arbitrary path using lines, arcs and Bézier curves. The path definition looks like an alphabet soup and is best left to be generated by a vector graphics software such as Inkscape. For SVG animations, you still have to understand it a little though, so here is a primer.
An example of quadratic and cubic Bézier curves:
M x,y new pen position (Marker)
L x,y Line to
Q cx, cy, x, y Quadratic Bézier curve to (x,y)and one control point
C cx,cy, dx,dy, x,y Cubic Bézier curve to (x, y) and two control points
A elliptic Arc
z at the end of the string, closes the path
Let us put some life in those vectors. You can see a demo of a little SVG guy flying through the clouds on a surf board if you follow this link and click on the picture.
The surfboard is bobbing up and down and the mouth of the character alternates between a big and a huge grin while its eyes are rolling around with dilating pupils, like any good surfer’s. These are the four types of possible SVG animations.
The simplest SVG animation uses the animate tag to animate one parameter of a geometric shape, in this example, the radius of the eye.
To make the pupil dilate, the values attribute lists radius values we want to animate between.
The nice thing is that the attribute you animate can also be the d of a path tag. This allows you to create an animated path. The only constraint is that the two curves you animate between must be of the same type and have the same number of control points; their definitions must be made of the same letters in the same positions, with only the numerical parameters changing. When moving the mouth of the character, only the “big smile” and “huge smile” positions are defined. SVG animations do the interpolation.
- animate attributeName=“d” dur=“2s” repeatCount=“indefinite” values=“m 0,0 c 1,15 -13,45 -45,45 -32,0 -44,-28 -44,-44 z; m 0,0 c -4,15 -66,106 -98,106 -32,0 3,-89 9,-105 z” /
Of course, SVG also has geometric transforms, which can be animated. The animation tag is, this time, called animateTransform. You have to tell it which transform you want to animate and provide a semi-colon separated list of values for all the key positions. Composing animated transforms is possible, too. You tell the browser to do so using the additive=”sum” attribute.
To animate geometric transforms:
The third and last SVG animation tag is also the most valuable. animateMotion is used to instruct an object to follow a path. It has a hidden little gem called the rotate=”auto” attribute. This makes the object not only follow the designated path but also orient itself to always face forward, as a car following a road.
The revolution of the eye is implemented by sending the pupil on an elliptic path using the animateMotion tag.
For example, this animated SVG button jumps with a moving drop shadow effect when clicked:
Switching from declarative to programmatic animation techniques, the canvas tag is your first choice. Of all the technologies described in this article, it has the best cross-browser support and browser vendors went to a lot of effort to make it capable of 60 fps animation. Here is how you set up a canvas:
The first size (tag attributes) sets the resolution of the canvas. That is the size of the coordinate space you will be using. The second size (CSS properties) is the size of the canvas rectangle as it appears on the screen. Wouldn’t it be nice to set the physical size of the canvas to 100 per cent, keep the internal coordinate space unchanged, and have the browser render the drawing correctly for any window size? Unfortunately, browsers scale the contents of the canvas as a bitmap. Any upscaling results in a blurry mess of pixels. As a result, setting these two sizes to the same values is the only practical option.
Frame by frame animations
To animate the canvas, you need an animation loop, which you should base on the requestAnimationFrame() function (correctly vendor-prefixed!). This function gives the browser the ability to manage the frame rate or even stop the animation if the browser tab is hidden.
Typically, canvas is used for animations where the positions of objects have to be determined on a frame by frame basis, for example, because they result from a physics simulation. See a box2dweb.js demo. The simulation continuously changes the positions of objects in the simulated world and the animation loop periodically displays the current state of the world on screen.
SVG sprites in canvas
A useful trick would be to use SVG sprites in a canvas animation. A static vector picture of any complexity is really verbose in canvas. Doing so is actually possible although not as simple as sending an .svg to ctx.drawImage(). For obscure security reasons, only inline SVG is supported and you have to get to it using the Blob API (get the code).
The SVG sprite will be displayed at any scale in its vector beauty, but sadly, only in Chrome.
Fortunately, Three.js comes to the rescue. Three is a library originally written by Mr.doob for a couple of Chrome demos (www.ro.me / www.chaostoperfection.com) but it lives on its own. It has all the basic as well as exotic shaders already built in so that you can focus on the useful things: camera, lights and action!
The first thing you need to use is Three glue code (not fun, but simple enough):
So, before the lights and action, you create a camera and position it in your scene:
Next, let there be light:
Then we need something to display. Why not a cube? 3D objects in Three are called meshes. These are made of a geometry and a material.
Here, we use a simple texture:
And then, finally, we add all those things into a scene and call the render function.
This will produce a static image of our lit and textured cube. To make it move, we wrap the render call in an animation loop as previously and change the position of the cube on each frame.
Using a 3D modelling software such as Sketchup is recommended. Three understands several 3D model formats, including COLLADA (.dae) which has broad industry support. Here is how you load a model in Three:
With a little more effort, you can re-skin the droid with all the fancy shaders Three has to offer, including Lambert and Phong illumination, normal mapping, bump mapping, environment mapping and more.