I wanted to make some hand written SVG animations for quite some time now and came up with cute little one for my website with envelope transforming on click into a “check” inside a circle signalizing that the email has been stored in a clipboard. The preview and implementation is here:
https://codepen.io/0ctothorp/pen/xxWzZwB
Let’s walk through the code together. First, the markup. All the animation is defined declaratively with <animate>
elements documented here: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate. This is the so called SMIL element. SMIL is just a W3C specification for animations in XML documents. MDN is quite good at documenting web APIs, so I won’t go into much detail. Let’s get to the point.
If you’ve looked at the HTML in the codepen you’ll see that I haven’t used from
and to
attributes on any <animate>
elements. These seem quite natural to use and I’ve used them initially, but making the pausable and reversible animation with them is quite a task. keyTimes
and values
are a much better duo. In both of them you define a set of values separated by semicolons. Both of these attributes should have the same number of values, because these values match each other. keyTimes[1]
is the time at which an attribute will reach value of values[1]
etc.
Since there’s no native way of reversing the animation, I’m doing a little trick by defining the whole cycle upfront. That’s why every animation is defined with 3 values. We will just pause the animation when it reaches half of it and resume it on click, to make the reverse effect.
Also, the important bit here is the begin="indefinite"
attribute. It prevents the animation from running on the document load and waits to be manually started with beginElement()
method defined on the <animate>
’s javascript interface. Now you know why beginElement()
is called on every <animate>
in the document on click.
The next part is kind of ugly, but necessary:
setTimeout(svg.pauseAnimations.bind(svg), 500)
I’m setting the 500ms timeout, after which pauseAnimations()
is called on the svg element. Calling this method stops every animation happening on the svg element. It’s ugly, because I guess animation and setTimeout
clocks are not synchronized, so our pause might not be ideally in the half of the animation, but in case of this one it looks okay and there is no way of knowing if the svg animation reached a certain value. The animation events only exist for begin and end.
To resume animation the unpauseAnimations()
is called on the <svg>
and that’s it.
Fun fact: the begin
attribute of <animate>
element can also take in a string in following format: "<id>.<eventName>"
. The supported values are defined here: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/begin#event-value. Look at this example which I made before I implemented the “onclick” example:
https://codepen.io/0ctothorp/pen/YzaEQzW
I hope the examples and explanations helped. Happy svg animations hacking!