Basic SVG path tweening with SMIL
16 July 2013 by @ddprrt | Posted in: SVG
Sorry, your browser does not support SVG animations with SMIL.
I'm working on a tribute to one of my childhood heroes, the Caped Crusader, the Dark Knight, the world's greatest detective: Batman. And when I say childhood hero, I do mean a hero to this day. Anyhow, inspired by an EPS file i got over on DeviantArt, I wanted to create a history of his emblems from the very first to the very last, spanning all 73 years, much like this now infamous video did.
First I had the idea of just fading over the logos, but that's actually kinda boring, so I went back to a rad idea I used once back then when Macromedia Flash 4 was still in its early days: Tweening! (well, just like in the video, no?)
All right! To the Batcave, Robins!
A short thought on RaphaëlJS
The library also allows to animate between paths, and does it in a very interesting, jQuery-like way: Instead of using SMIL, RaphaëlJS interpolates path points between the start and ending state and constantly updates the path inside your SVG. I was stunned by the complexity of this rather powerful algorithm, but looking at it from a performance point of view ... nah, you get the same issues you love to hate from jQuery.
RaphaëlJS is good if you don't want to delve to deeply into drawing programs or SVG source code, and I used it mainly to apply certain transformations on exiting SVG paths, and then copying the new SVG result. But for my tribute page I dropped it completely and just used it as a fallback for IE, because SMIL is still not implemented and looking at the current preview of IE11, will not be landing there for quite some while.
The first animation
My work is based on a great animation done by Tavmjong Bah. In his blog post he give additional information on how he actually implemented it. Some important parts were: The SVG paths you want to transform have to be in the same pattern, otherwise you don't get any animation at all:
Not even those funky effects we know from Flash back then, which is one of the main advantages of RaphaëlJS: The algorithm interpolating between two paths might lead to quirky results, but is nonetheless bloody good!
Anyhow, I did want to stick to SMIL, but even by using Tavmjongs data I wasn't able to recreate one transition between two bats. It took me some time to realize how Tavmjong was implementing his animation. Mostly because I didn't take a good look at the values. The
<animate>-element is pretty straightforward, but the values do need some explanation: To create an animation from path A to B, the values inside the element have to feature both paths, separated by a semicolon. So if you want a transition from Figure A to B, you first have to include the path in your
<path>-element, and then again as the first value tuple in your animation:
Actually, I lied a little bit. You don't need to include the path inside the "d" attribute of the
<path>-element. The animation will work fine even without it. But: if you include the path data directly you can do some more, event-based stuff with your SVG. But more on that later. First, check on some of the attributes of the
Some parameters are already visible in the example above:
duris short for "duration" and defines exactly that. Use any value in seconds for that.
repeatCountallows us to define how often the animation is going to be repeated. It takes any number, or
indefinitefor endless repeat.
attributeNamedefines which attribute of the original element is going to be animated. Since the
<animate>tag can be used on a multitude of SVG elements, this parameter has a multitude of possibilities. We use the "d" (very short for "path data"), since this is the parameter of the original
- Last, but not least we defined a set of
We are not done with that. One thing you might realize is that the animation always jumps back to it's initial frame (which is why we also need to define the original path in the parent
<path> element). To make sure that the ending state is preserved, we add another attribute called
fill and set its value to
freeze. In other elements,
fill is used to define the filling color, in animation it's the state at the end.
Trigger the animation by clicking or tapping on it.
TriggersAs you've seen, animations can be triggered on certain actions. Use the
beginattribute to define the interaction or property which starts the animation, as well as
endto define the interaction which should stop it. And this is where this stuff becomes really good, as you can add at least some control to your animation. You can either use DOM events for that, like
click(as shown in the example above) or
mouseover, but you also can use time constraints to apply a certain delay:
These parameters take almost any input based DOM event, but with one very special constraint: The SVG data has to be embedded in the DOM. If you have your SVG in a file and are referencing it in an image tag or whatever, the DOM events won't trigger.
Trigger events can be expanded further by not only using the DOM event of an element itself, but also by referencing to an event by another element. For instance,
begin="button.click" allows us to trigger the animation once a certain element with the id of
button has been clicked.
This gives us a multitude of possibilities. Look at that the following example:
Here we start the second animation once the first one has already started.
Click on the left bat to see the magic happen.
Sorry, your browser does not support SMIL events
<animate>-element and changing the original path.
I dropped this idea due to browser constraints, tough it would've been easy: Simply add the callback into your markup, or use
addEventListener to achieve the same.
Other events include
onbegin which is obviously triggered when the animation starts, and
onrepeat, which counts the number of interations the animation has run and fires every time one iteration is complete.
Start the animation by clicking or tapping the bat!
Again, this will work only if the SVG is included directly in your DOM.
As you all know, we just should feature detect to check if we're able to use SMIL. However, it might be that you get a false positive in IE9, according to this (somewhat old) Modernizr issue, so be aware!
With that one you can detect SMIL:
Place it in a try-catch block! To check for event callbacks, this is how it should work.
However, not even Firefox provides interfaces in their DOM API. This is a workaround: Add an almost empty SVG markup to your DOM and have it call a function once it starts. In this function, set your bools or classes, or whatever you like or need.
This blog entry is based on about a weekend of research on that topic, fooling and playing around with values and constantly checking the specification. And I'm sure that I just scratched the surface! I stumpled upon parameters such as
calcMode, which I didn't read in depth. Also the possibility of an
animationPath is available, where I've no bloody clue how they can be created whatsoever. SVG animations are a beast of their own, and very powerful once you understand them.
Use is limited, tough. Microsoft has no plans of including SMIL in their current versions of IE. The spec has been out there for a while, but even the preview version of IE11 shows no sign of implementation. Furthermore, if you want to use that stuff on mobile browsers, be aware that performance might be below your expectations. Even iOS Safari, which is still one of the most performant mobile browsers out there, has a clear performance issue when it comes to basic tweening. It's not as bad as animating with RaphaëlJS, tough, because there you just won't get any animation at all.
Comments? Shoot me a tweet!