09 Mar 2011 Editor's note: Some browsers support SVG animations. These animations were tested in Microsoft Internet Explorer with the Adobe plug-in, Google Chrome, and Mozilla Firefox. The sample animations worked best in Internet Explorer with the Adobe plug-in. Part of the animations worked in Google Chrome. In Download, find the sample SVG files in x-matters42-examples.zip and HTML files that embed the sample SVG files in x-matters42-html-examplefiles.zip.
SVG has been a W3C recommendation for some time now but has been slow to catch on in the wider Web. That might be changing now that Mozilla (Firefox), Apple (Safari), and Opera all support (or are about to support) SVG in their browsers, without plug-ins. The Linux desktops Gnome and KDE also support SVG for use in themes, icons, backgrounds, and games. Until now, the main way to view SVG was to use the Adobe plug-in (see Resources), which I used to test the example code.
What exactly is SVG, and when is it appropriate to use? SVG has been compared to PDF, PostScript, Flash, and HTML, but it has elements of all without duplicating any of them. SVG is complex, which allows it to be many things to many people while remaining a text format (Google can index it) and XML (you can manipulate it with the DOM). At the most basic level, it is an XML language for describing vector graphics -- pictures made by drawing lines (as opposed to bitmap graphics -- pictures made by drawing pixels). But SVG can also contain and manipulate bitmaps, text, even sound files. In addition, it can:
- Describe ways to apply noise and other effects to vector or bitmap images
- Specify elaborate color gradients and patterns
- Provide links to other parts of the Web
- Be scripted and transformed, animated, and styled
- Be interactive in many ways.
I want to follow up on David's earlier XML Matters column on SVG by talking about SVG animation and interactivity, specifically its declarative animation and interactivity.
Declarative programming 101
SMIL (see Resources) -- a language for mixing video, pictures, music, and text in real time -- has been adopted and extended by SVG. Much of the declarative animation in SVG is borrowed directly from SMIL (without using the SMIL namespace), although some aspects of SMIL were left out because they made no sense in the context of SVG, and others were extended. SMIL is used in RealNetworks' RealPlayer, in Apple's QuickTime, and (as XHTML+SMIL) in Microsoft's Internet Explorer. SMIL is a tool for choreography of these different media formats in response to time and events.
The declarative capability that SVG inherits from SMIL lets you animate graphics (for example) either at predetermined times, or in response to certain events. You can declare five types of animation and use many events to trigger your animations.
It's time to show you some examples.
Drawing a blank
The first example creates a canvas for the rest of the examples in this article. I'm going to drift back to grade school and use sketches of rockets and UFOs, so the background will be a simple sheet of lined notebook paper with three punch holes. This example doesn't use animation. I merely set up a canvas reminiscent of a grade-school notebook, ready for doodles. The paper is quite simple, consisting of a single
svg element containing a short section of definitions: a line pattern and a small circle. Then, in the body (outside the
defs) I draw a rectangle over the whole page, filling it with the line pattern. Then I grab the circle I just defined and draw it in three places. Listing 1 shows the SVG code for the notebook-paper example.
Listing 1. A sheet of notebook paper
<?xml version="1.0" encoding="utf-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" fill="white"> <defs> <pattern id="single_line" x="0" y="0" width="1" height="32" patternUnits="userSpaceOnUse" viewBox="0 0 1 32"> <line x1="0" y1="32" x2="1" y2="32" stroke="lightblue" stroke-width="0.5"/> </pattern> <circle id="single_hole" width="28" height="28" cx="14" cy="14" r="14" fill="white" stroke="black" stroke-width="0.5"/> </defs> <rect x="0" y="0" width="100%" height="100%" fill="url(#single_line)" stroke="none"/> <use xlink:href="#single_hole" x="32" y="10%"/> <use xlink:href="#single_hole" x="32" y="50%"/> <use xlink:href="#single_hole" x="32" y="90%"/> </svg>
The second example adds one
path element to the definitions and uses it once in the body of the
svg (see Listing 2). The path is made up of a move statement (
Mx y) and line-to statements (
Lx y) in absolute coordinates. (Statements using relative coordinates would be lowercase.) The
d attribute contains 1,072 statements (gathered through Wacom tablet), so I've trimmed it for brevity in Listing 2. You can view the full file all its glory in the example code (see 02_rocket_static.svg in Download).
Listing 2. A rocket ship (changes only)
<defs> [...] <path id="rocket" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" stroke="#4C4C4C" fill="white" d="M23 8 L21 11 L21 11 L20 16 [etc...]"/> </defs> [...] <use x="50%" y="50%" xlink:href="#rocket"/>
So far, my doodle is as static as the virtual paper it's sitting on (see Figure 1), but that will change in the next example.
Figure 1. The rocket
At last it's time for some motion. The rocket will follow a circle to demonstrate some of the features you can expect from SVG: path following and auto-rotation. This third example demonstrates the first type of declarative animation I'll cover -- the
animateMotion element (see Listing 3).
Listing 3. A rocket ship on the move (changes only)
<defs> [...] <path id="rocket2" transform="rotate(90)" [...]/> <path id="circle" stroke-width="4" fill="none" stroke="#000000" d="M100 350 A300,300 0 1,1 700,350 A300,300 0 1,1 100,350"/> </defs> [...] <use xlink:href="#rocket"> <animateMotion rotate="auto" dur="30s" repeatCount="indefinite"> <mpath xlink:href="#circle"/> </animateMotion> </use>
defs element I've added a
path element that defines a circle for the rocket to move around. I've also added a
rotate transform to the rocket definition so that the rocket will face the right way as it goes around the circle. SVG can turn an image so it always faces the tangent of the line it is following. I chose a circle because my hand-drawn paths made the tangent jump around. Someone with a steadier hand on the tablet might achieve better results. The important thing is that the line the animation follows can be any arbitrary path. Also note that instead of the simpler
circle element, I have created the circle using a
path with elliptical arcs, so the rocket has a path to follow with a beginning and an end.
Okay, so the rocket spinning around gets old after a while. In the fourth example I spice up the scene with a visitor from another world, in a handy-dandy UFO (see Figure 2). This gives me the chance to demonstrate the vanilla
animate element as well as its sibling,
Figure 2. Unidentified flying vectors
Listing 4 shows all the changes that create the UFO.
Listing 4. UFO (changes only)
<defs> [...] <g id="ufo"> <path id="ufo-body" d="M0 50 A100 100 0 0 0 100 50 L100 40 A100 100 0 0 0 0 40 z"/> <circle id="port-1" class="port" cx="25" cy="45" r="6"/> <circle id="port-2" class="port" cx="50" cy="45" r="6"/> <circle id="port-3" class="port" cx="75" cy="45" r="6"/> </g> <path id="back-and-forth" d="M100 50 L700 50 z"/> </defs> <use xlink:href="#ufo"> <animateMotion dur="50s" repeatCount="indefinite"> <mpath xlink:href="#back-and-forth"/> </animateMotion> <animateColor xlink:href="#port-1" dur="10s" repeatCount="indefinite" attributeName="fill" values="red;green;blue;red" begin="ufo.load"/> <animateColor xlink:href="#port-2" dur="10s" repeatCount="indefinite" attributeName="fill" values="green;blue;red;green" begin="ufo.load"/> <animateColor xlink:href="#port-3" dur="10s" repeatCount="indefinite" attributeName="fill" values="blue;red;green;blue" begin="ufo.load"/> </use> [...]
defs, I've added a
ufo formed from simple SVG arcs and circles, in order to avoid another huge block of
path statements from the tablet. I've also added a
path to follow for moving back and forth -- a simple line. The closing
z closes the path so the UFO has something to follow back to its starting point. In the body, I have a more complex
use element than before which contains four different animation elements: an
animateMotion similar to what you've seen with the rocket, then three
animateColor blocks. Interestingly, the
animateColor elements do not apply to the
ufo directly (which would be the default), but instead target the UFO's individual portholes by using
xlink:href attributes. I've also tied the start of each
animateColor to the loading of the UFO's main element.
Rockets versus UFOs
The fifth example adds user interaction, still through declarations. SVG supports all of the mouse events you expect but also has support for keyboard events -- even for specific key events, which is what I've chosen to demonstrate here. Each letter of the alphabet triggers a rocket headed for space (see Figure 3). Time to defend your home planet from the UFO invader!
Figure 3. Missile defense
For this example I remove the
transform attribute from the rocket definition, because the rocket will no longer be going in circles, and I remove the definition for the circle. No other changes to the
defs are needed. All the action this time is in the body, with 26 variations on a theme (see Listing 5).
Listing 5. Rockets versus UFOs (changes only)
<use xlink:href="#rocket" x="4%" y="85%" display="none"> <set id="showa" attributeName="display" to="inherit" dur="5s" begin="accessKey(a)"/> <animate dur="5s" begin="showa.begin" attributeName="y" from="600" to="-100"/> </use>
Each variation uses the same rocket I defined earlier, but ties it to a different
x coordinate and an alphabetic keyboard event. When the viewer presses a key, it triggers the
set element, which makes the rocket visible at the bottom of the screen. Notice how an ordinary
animate element is tied to the start of the set event. This demonstrates how events can trigger other events. If you want chained events, the second event can be triggered by the end of the set event, rather than the beginning.
To run this example in your browser, download the example code (05_rocket_defense.svg in Download). If you have installed an SVG viewer, click to see this animation. Remember that you need to press a letter key or keys to trigger the rocket motion; depending on your computer's speed, you might experience a slight delay before the rockets appear.
The final example completes your tour of declarative animation scripting in SVG with the
animateTransform element. This example shows five rocks, or groups of rocks, appearing in the center of the page, hurling out toward the viewer and disappearing off the edges (see Figure 4).
Figure 4. Rocks coming right at you
This effect requires the animations both to nest and to influence one another. The
additive="sum" attribute allows the effect of the various transforms to be cumulative. The first inner transform rotates each rock on its own axis, so that the rocks appear to be spinning as they approach the viewer. The second inner transform scales each rock so it grows (simulating the approach). Both of these transforms are applied to the rock's path, which I pull in with the
use element. The outer transform, applied to the entire grouping, moves the rock out toward an edge of the view and off-screen.
Listing 6 shows only one group of animations, but the others are similar, with different points to move offscreen to, and tweaks to their starting time and duration, to mix things up. Most of the line data in Listing 6 is snipped for brevity.
Listing 6. Asteroids! (changes only)
<defs> [...] <path id="rock1" d="M-8 -21 L-9 -21 L-15 -21 [...] /> <path id="rock2" d="M19 -15 L18 -17 L14 -20 L12 [...] /> <path id="rock3" d="M-17 -20 L-18 -20 L-19 -20 [...] /> <path id="rock4" d="M11 -14 L11 -15 L10 -15 [...] /> <path id="rock5" d="M14 -30 L13 -30 L12 -29 [...] /> </defs> [...] <g> <use xlink:href="#rock1"> <animateTransform additive="sum" attributeName="transform" type="rotate" from="0" to="360" dur="6s" repeatCount="indefinite"/> <animateTransform additive="sum" attributeName="transform" type="scale" from="0.1" to="6.0" dur="6s" repeatCount="indefinite"/> </use> <animateTransform additive="sum" attributeName="transform" type="translate" from="600,400" to="1400,-200" dur="6s" repeatCount="indefinite"/> </g> [...]
"Simple things should be declarative. Complex things should be procedural." --Adam Bosworth
|Example SVG code||x-matters42-examples.zip||14 KB|
|Example HTML files||x-matters42-html-examplefiles.zip||2 KB|
- Scalable Vector Graphics (SVG) 1.1 Specification: Read the current W3C Recommendation.
- SVG.org: Check out this community Web site for SVG users, developers, and enthusiasts.
- Synchronized Multimedia: Learn more about SMIL at the W3C's Interaction domain.
- SVG Programming: The Graphical Web by Kurt Cagle (Apress, 2002): This is a comprehensive guide to SVG programming.
- "XML Matters: Program with SVG" (developerWorks, April 2005): Read David Mertz's previous XML Matters column, which introduces SVG.
- "Introduction to Scalable Vector Graphics" (developerWorks, March 2004): This tutorial by Nicholas Chase introduces the concepts necessary for building SVG documents, including basic shapes, paths, text, painting models, animation, and scripting.
- "Add interactivity to your SVG" (developerWorks, August 2003): Get a good roundup of SVG interaction techniques with this article by Brian Venn.
- "Bring Scalable Vector Graphics to life with built-in animation elements" (developerWorks, June 2003): Brian Venn shows you how to apply the five flavors of SVG animations to your SVG documents.
- Adobe's SVG Zone: Find many SVG resources.
- XML Matters column: Find other articles by these authors.
Get products and technologies
- Adobe SVG Viewer: Download the viewer that was used to test the examples in this article.
- Batik: Try this Java™ technology-based SVG toolkit from the Apache-XML project.
- Compound XML Document Editor: This standards-based, model-driven editor for mixed-namespace XML documents includes support for SVG and SMIL.