Prerequisite: Introduction to SVG
David Dailey, An SVG Primer for Today’s Browsers:
Read enough of this, and carefully enough, that you are able to meet the learning objectives immediately below.
Be able to:
<animate>, <animateMotion>, <animateTransform>.<set>).Declarative animation in SVG is achieved through the use of Synchronized Multimedia Integration Language (SMIL), pronounced like “smile”. Declarative animation is a much higher-level approach than JavaScript animation. You cannot do everything with SMIL that you can do with a scripted animation; but if you can do it in SMIL, it will usually be much simpler, because you do not have to worry about the fine details of timing (using timeouts and interval timers), and it might also give a smoother, more consistent animation.
In the SVG Study, observe the “train” (well, car) moving along its track, the rotating ellipse, the pulsating rounded rectangle, and the color change as you mouse over the four-pointed blue star. All of these animations are declaratively specified.
To animate a graphical element, such as a rectangle or circle, place one or more <animate> tags within the element.
For example, if we want a rectangle to go from x = 12 to x = 688 over a period of 6 seconds1, we can write:
<rect x="12" y="12" width="100" height="100">
<animate attributeName="x" values="12;688" dur="6"/>
</rect>The
attributeName: the name of the attribute you want to animate. For example, to control the x coordinate of a rectangle, attributeName="x".
values: a list of values for the attribute, separated by semicolons (;). Spaces before and after the semicolon are allowed. Values may be given as raw numbers (pixels) or as percents (relative to the SVG element? to the parent element?).
from="value1" to="value2" instead of values="value1; value2".dur: the duration, or time it takes to go through the list of values.repeatCount: how many times you want to go through the values list, either a number (such as "10"—in quotes, since this is XML) or "indefinite".fill="freeze" (optional): makes the attribute stick at the final value at the end of the animation. Without this, it returns instantaneously to the initial value.You can give more than two values for the animated attribute. For example, to make an object go back and forth, left to right to left, you could animate x with values="12; 688; 12". With dur="3", we would have x = 12 at the start, x = 688 at 1.5 seconds (50% of the duration), and x = 12 again at 3 seconds. With four values, it would take 1/3 of the duration to go from each value to the next.
If you don’t want the transitions between values to receive equal time, you can use the technique of key framing which pairs each value with a key time. The key times are fractions (between 0 and 1) of the duration. So,
<animate attributeName="x"
values="50; 250; 450; 550"
keyTimes="0; .1; .9; 1"
dur="10"/>would move from x = 0 to x = 250 in 1 second (.1 × 10); in 8 more seconds, at t = 9, we would have x = 450; and in 1 more second, at t = 10, we would have x = 550.
The velocity changes are instantaneous. If you want to smoothe them out, you can use calcMode="spline" with a keySplines list, for something like a Bézier curve in the time domain. The tutorial does not do a good job of explaining this, and I am not even going to try. If you want to look into it more, the best exposition of it I can find is here; but this is strictly optional.
You can animate more than one attribute of an element at the same time.
We can make an object follow a path, i.e., a curve formed by a <path> element. Since the path is complex, it would be horrible to have to calculate the x and y coordinates of the motion. Fortunately, we can let SMIL do that calculation for us!
We need a path with an id to reference it:
<path id="path1" d="M 0 500 ..." />If we don’t want to see the path, we can specify visibility="hidden":
<path id="path1" d="M 0 500 ..."
visibility="hidden"/>Then, create a rectangle, and use an <animateMotion> child element (instead of <animate>) to make it follow the path:
<rect x="0" y="0" width="25" height="10" ...>
<animateMotion dur="10" repeatCount="indefinite">
<mpath xlink:href="#path1"/>
</animateMotion>
</rect>Alternatively, the <animateMotion> can be created externally to the element that it animates, and have a reference to it:
<rect id="rect1" x="0" y="0" width="25" height="10" .../>
<animateMotion xlink:href="#rect1" dur="10s" repeatCount="indefinite">
<mpath xlink:href="#path1"/>
</animateMotion>Transforms include translation, scaling, and rotation. Recall that these are specified (without animation) as
transform="translate(dx [dy])"transform="scale(sx [sy])"transform="rotate(angle [cx cy])"where brackets ([]) indicate optional parameters.
Animating transformations requires an <animateTransform> element. It works like this:
<animateTransform attributeName="transform" type="translate"
values="dx1 dy1; dx2 dy2; ..."
.../>
<animateTransform attributeName="transform" type="rotate"
values="angle1 [cx1 cx2]; angle2 [cx2 cy2]; ..."
.../>
<animateTransform attributeName="transform" type="scale"
values="sx1 [sy1]; sx2 [sy2]; ..."
.../> Of course, you want to fill in some of the “…” with dur, repeatCount, etc. If you want to combine transforms—for example, animate both translation and rotation—then you need to toss additive="sum" into the brew as well; otherwise, each <animateTransform> will simply replace all previous transforms (whether animated or not).
You may not always want an animation to begin as soon as the page is loaded, and you may not always want it to either run forever or to end after a fixed duration or number of repetitions. Rejoice! SMIL allows us to start and stop animations using events, delay times, and combinations thereof.
To start an animation 10 seconds after the page is loaded, you could use
<animate begin="10" .../>Events are similar to JavaScript events; they include clicking on an SVG element (click event), and moving the mouse into an element (mouseover) or out of it (mouseout). Yes, we can have SVG “buttons.”2
<animate begin="click" .../>Unlike JavaScript, where you set up an event handler (callback function) for the element that is the source of the event (for example, the button that is clicked on), in SMIL you declare how to handle the event in the element that responds to it (the element to be animated or changed). Consequently, unless these are the same element, you must also specify the source of the event. Notice that, contrary to the usual practice, a # is not required here to reference the id of the source element.
<rect id="trigger" .../>
<rect ...>
<animate begin="trigger.click" .../>
</rect>To stop an animation prematurely, use end="TIME" or end="EVENT".
These begin and end attributes can be used in the <animateMotion> and <animateTransform> tags, as well as <animate>, and in <set> (covered below).
You can cause an animation to begin or end after an event, with a specified delay time, with notation such as begin="EVENT+TIME".
You can cause an animation to begin or end in response to any of a number of events, using ; (meaning “or”) to connect the events, with notation such as begin="BEGIN1; BEGIN2".
The beginning and ending of an animation are themselves events (begin, end), which can trigger other animations. So, to have a second animation start 5 seconds after the one identified as anim1, you could write begin="anim1.begin+5".
While animations usually vary a parameter value smoothly over a period of time, sometimes we want to make an instantaneous change to an attribute. That’s what the <set> element is for:
<set attributeName="ATTR" to="VALUE" begin="BEGIN"/>For a series of changes, it is also possible to specify an animation with calcMode="discrete". This causes the attribute to “jump” to the next value, instead of moving gradually from one value to another.
JavaScript can also stop start animations, and can receive and respond to SMIL events—but we’ll save that for the next lesson.
A duration of 6 seconds can be expressed as "6" or "6s". The default unit is s (seconds). Other units are possible: "0.1min" (1/10 minute) or "6000ms" (6000 milliseconds), for example; but would you really want to use any measure but seconds?↩︎
However, I wouldn’t recommend getting carried away with SVG buttons, or simulating other HTML elements in SVG. Standard HTML elements are likely to be more accessible to (i.e., usable by) vision-impaired visitors using screen-reading technology. So stick to HTML form elements, unless there is a cogent reason for switching to SVG.
And speaking of accessibility, how can we provide the equivalent of an <img alt="..." attribute for an SVG element? By using an actual <img src="foo.svg" alt="..."/> instead of inline SVG?