by Antoine Quint | Pages: 1, 2
Getting Our Mouse To Work
So far the work we've done, while valuable, is still a little lame. Niklas had his cubes reacting to mouse events generated when the user rolled over them. Let's get that to work, too. SVG animation provides a mechanism for reacting to mouse events. You might have noticed that in our previous animation we did not specify when our animation started. There is an attribute "begin" that would do exactly that, but it has a default value of "0s", which means our animation starts when the document is loaded. Here we want our animation to start when we mouse over the cube. Let's try introducing the "begin" attribute to our animation and set it to "mouseover", and see what it does:
<use xlink:href="#cube" transform="translate(307.5, 105.75)"> <animate attributeName="y" begin="mouseover" dur="2s" values="0; -45; 0; 16; 0; -7; 0; 3; 0; -2; 0; 1; 0" /> </use>
If you tried rolling your mouse over the cube you will have noticed it did start the animation. You might also have noticed that there is a bit of a strange behavior going on. If you don't move your pointer away from where you entered the cube and started the animation, then the animation will restart from the beginning when the cube falls down and goes under your pointer again. This is not what we want; we would rather have the cube not take any mouse event until the animation is finished. Once again, SVG animation provides the exact mechanism we are looking for: the "restart" attribute. By default its value is set to "always", which means that the animation is restarted at every time the situation indicated by the animation "begin" attribute is true. Another possible value is "whenNotActive", which according to the spec means that "the animation can only be restarted when it is not active [and that any] attempts to restart the animation during its active duration are ignored". This sounds like what we have been looking for, let's see how it goes:
<use xlink:href="#cube" transform="translate(307.5, 105.75)"> <animate attributeName="y" begin="mouseover" restart="whenNotActive" dur="2s" values="0; -45; 0; 16; 0; -7; 0; 3; 0; -2; 0; 1; 0" /> </use>
This is exactly the behavior we wanted. Excellent. However, we still have some way to go to a refined animation like the original. Let's move on to better things.
Upping the Pace
The key ingredient to make our animation as spiffy as Niklas's is to give the animation a acceleration/deceleration, gravity-like feel. When I opened the original source file in Flash, I noticed Niklas did two things. The first thing was adjusting the keyframes so that the animation would have different intervals between two sets of values. The other was to have easing-in and easing-out on certain intervals of the animation. Let's see how to implement that in SVG: keep your eyes peeled as this is really is the hottest part of what SVG animation offers.
Our first task is to get the values to be taken into account at non-identical intervals in time. It is time to introduce a new attribute to our growing set of friends: the "keyTimes" attribute. This attribute will help us recreate the timing that was going on in the original SWF animation; it will help us create a mapping between the "values" attribute and the time at which each value will be reached in our animation. The SVG specification says that "each time value in the keyTimes list is specified as a floating point value between 0 and 1 (inclusive), representing a proportional offset into the simple duration of the animation element". Albeit in formal terms, this is what we are after. Let's give it a shot:
<use xlink:href="#cube" transform="translate(307.5, 105.75)"> <animate attributeName="y" begin="mouseover" restart="whenNotActive" dur="2s" values="0; -45; 0; 16; 0; -7; 0; 3; 0; -2; 0; 1; 0" keyTimes="0; 0.2564; 0.5128; 0.6154; 0.6923; 0.7436; 0.7949; 0.8462; 0.8974; 0.9231; 0.9487; 0.9744; 1" /> </use>
Pay no attention to the weird values we set in the "keyTimes" attribute. These were computed by hand from the original Flash animation. It looks kind of odd because Niklas's keyframes were placed themselves at odd times. This is of course a job best suited for an SVG-enabled animation authoring tool, but here we're trying to figure out how SVG animation works exactly so it is worth doing by hand. The result we have now is somewhat paced animation. It is not as refined as Niklas's yet, but the end bit looks exactly like it ought to. As for the start of the animation, this is where Niklas had the easing-in and easing-out take place. Now watch out, because this is getting really smooth.
In Macromedia Flash 5, one can specify easing-in and easing-out between two keyframes with a percentage ranging from "-100%" to "100%". Although this is quite a jolly nice control over acceleration, SVG provides a much sweeter mechanism. Instead of having a simple percentage, SVG allows us to describe the speed behavior of an animation interval with a two-point Bezier curve (you've probably heard a lot about those if you've got any experience with SVG). What does this mean?
Draw a Bezier spline that fits in a square, starting bottom-left going top-right. The rule to draw that kind of curve is that it has to be bijective (a unique mapping of x and y). You can find quite a few of these curves just above section 19.2.8 of the spec, which are really helpful in visualizing how keySplines work. Now the x-axis represents time and the y-axis represents the percentage of the variation between the two values. That allows us to have very fine control of animation speed. While Flash only allowed us to specify constant speed variations, SVG enables multiple speed variations in one given interval. Now that we have a better idea of how keySplines work, let's put our knowledge to work:
<use xlink:href="#cube" transform="translate(307.5, 105.75)"> <animate attributeName="y" begin="mouseover" restart="whenNotActive" dur="2s" calcMode="spline" keySplines="0 .75 .5 1; .5 0 1 .25; 0 .25 .25 1; .5 0 1 .5; 0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1" values="0; -45; 0; 16; 0; -7; 0; 3; 0; -2; 0; 1; 0" keyTimes="0; 0.2564; 0.5128; 0.6154; 0.6923; 0.7436; 0.7949; 0.8462; 0.8974; 0.9231; 0.9487; 0.9744; 1" /> </use>
Now this looks nice, much like the real thing, in fact a lot smoother since we did not have to worry about the cumbersome frame-rate. Besides the "keySplines" attribute we just introduced, you will have noticed a new friend: "calcMode". This attribute enables tweaking of the way the interpolation between the animation values we set is done. So far, we have use its default value ("linear") and now we needed to set it to "spline" so that it would take into account our "keySplines" attribute.
Go Forth And Multiply!
One of the last things missing is actually having the sixteen instances of our cube. Since we defined our cube as an SVG "symbol", we only have to place sixteen "use" elements, much like we have used so far with our single cube. I'm not going to teach you how to line cubes up, I actually got Illustrator to give me the bounding box of the cube and did a rough alignment by hand (you can see it's not perfectly aligned when you zoom in).
In much the same way as we created a symbol for the graphics in order to create a single definition for multiple re-use, we may well want to have references to our animation since all the cubes are animated in the same way. We could have had our "animation" element as part of the symbol, but the SVG 1.0 specification states that this would result in all instances of the symbol being animated at the same time. However, animation referencing is an issue being addressed by the W3C SVG Working Group for future enhancements, and SVG 1.1 (currently in Working Draft status) introduces a mechanism allowing for animation of animated symbol instances only. Still, there is a way to actually re-create this kind of behavior, using entities. The idea is to have all the sensible values of our animation, which may be changed in our authoring, be centralized so that a single change would be applied to all the animations. Here goes:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" [ <!ENTITY dur "2s"> <!ENTITY values "0; -45; 0; 16; 0; -7; 0; 3; 0; -2; 0; 1; 0"> <!ENTITY keyTimes "0; 0.2564; 0.5128; 0.6154; 0.6923; 0.7436; 0.7949; 0.8462; 0.8974; 0.9231; 0.9487; 0.9744; 1"> <!ENTITY keySplines "0 .75 .5 1; .5 0 1 .25; 0 .25 .25 1; .5 0 1 .5; 0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1"> <!ENTITY calcMode "spline"> <!ENTITY begin "mouseover"> ]> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 665 250" xml:space="preserve"> <!-- <title>, <defs> and background <rect> as we have seen --> <g id="cubes" transform="translate(300.25, 143.45)" > <use xlink:href="#cube" transform="translate(0,-72)"> <animate dur="&dur;" values="&values;" keyTimes="&keyTimes;" keySplines="&keySplines;" attributeName="y" begin="&begin;" restart="whenNotActive" calcMode="&calcMode;" /> </use> <!-- fifteen other <use> --> </g> </svg>
Et voilą!. This might well look like a dirty trick, but it does the job quite nicely. Now if we decide to change the keySpline for the third interval of our animation, we only have to edit it once. As for the animation itself, it looks like we're done. It should look very much like the original SWF version. In fact, Adobe's SVG Viewer on my laptop gives me a much smoother animation than the SWF played in the Flash player. By the way, the gzipped version (commonly known as SVGZ) of the final animation is only 891 bytes, while the SWF version is 1.29 KB. Prettier and smaller!
Wrapping It All Up
I hope you enjoyed this ride through some of the nicest features of SVG animation. There is a lot more to SVG animation, especially the idea of synchronization, which we'll consider in future articles. I think this simple animation highlights some of the key concepts of SVG animation, primarily that it is time-based and property-based, as well as some of its differences from the SWF animation capabilities.
- Great article!
2002-07-15 18:13:23 Tom Hart
- Input Text
2002-05-08 11:33:17 Mark Zartler
- Show me the way to join SVG and ASP.
2002-05-06 22:19:07 BYOUNG HEE HA
- Prettier and smaller!????????
2002-04-19 00:39:19 Muzak Deezign
- Prettier and smaller!????????
2002-07-15 19:40:49 Tom Hart
2002-04-18 09:42:26 Pete Fairhurst
- Going wireless
2002-04-22 03:32:59 Muzak Deezign
2002-02-18 08:30:59 jamie godin
- loading svg
2002-02-21 00:56:01 Antoine Quint
2002-02-14 21:31:15 Hedley Finger
- Animation static in Opera
2002-03-13 01:46:59 tom p
- Animation static in Opera
2002-02-17 06:58:32 Antoine Quint
2002-02-05 20:17:02 Neeraj Kumar
- Good Article but can't be compared to flash as of now.
2002-02-06 00:52:27 Antoine Quint
2002-02-01 02:44:41 Peter De Keer
- Difference with original
2002-02-01 07:46:25 Antoine Quint
2002-01-30 02:59:16 Daniel Fournier
- Last samples not working
2002-02-01 06:51:13 Antoine Quint
2002-01-29 13:36:59 Bruce Epstein
- Inaccurate portrayal of Flash/SWF
2002-02-01 04:14:40 Robin Berjon
2002-01-27 17:53:26 Patrick Nestor
- Does not work with my IE6
2002-01-28 15:30:14 Antoine Quint
2002-01-26 22:19:40 benjamin moss
- great article.
2002-01-29 03:57:19 Antoine Quint
2002-01-24 16:52:15 Alan Carlyle
- Embbeding the SVG Examples
2002-01-25 02:40:45 Antoine Quint
2002-01-24 07:30:15 Peter Herndon
- Cannot view SVG -- add MIME type to server
2002-01-24 14:13:04 Antoine Quint