Animated SVGs: Custom easing and timing
The chart above is an animated SVG featured on Sprout.
This chart, and one other animation on Sprout, were initially GIFs. By using animated SVGs instead of GIFs we were able to reduce our page size from 1.6 mb to 389 kb, and reduce our page load time from 8.75 s to 412 ms. That’s a huge difference.
Below, I’ll break down the animation of one of the circles seen in the chart. The technique applies to all of the elements in the graphic. With this you can create your own lightweight animated graphic.
Open your shape
If you created your shape with a vector program, it’ll likely be self-closing:
Open it up and include an
animateˆ element inside:
<circle> <animate /> </circle>
Our circle looks like this. Each of the properties I walk through below apply within the
<circle fill="#FFFFFF" stroke="#B450FF" stroke-width="6" stroke-miterlimit="10" cx="153" cy="127" r="6"> <animate
Select an attribute
attributeNameˆ is the attribute that we’ve chosen to animate. This could be opacity, stroke-width, width, or a number of others. We’ve chosen to use
cy, it refers to the y position at the center of our circle.
From and To positions
toˆ properties tell the shape where to start and end (in terms of the attribute we’re animating). I want this animation to loop seamlessly so the
to are the same value. (Note: These are the same as the
cy in my
Set a Begin time and Duration
valuesˆ refer to the attribute that we’re animating. Because we set our
cy above, these are the
There are 3 positions this circle will take in the animation (at 127, 117, and 90 units). We repeat those values so the circle will pause (though really it’s just animating to the same position). The 7th value completes the last pause and ends our loop.
Notice that the last values match the first value, and that those match our
Aside: Using duplicate values is one method for inserting a pause in an animation. Another, probably more proper method, is to create separate
animate elements for each of the movements and daisychain them together with ids, .endˆ, and an offset value. I used the duplicate values instead to reduce the number of elements in my SVG.
Easing the transition
keySplines=" 0.1 0.8 0.2 1; 0.1 0.8 0.2 1; 0.1 0.8 0.2 1; 0.1 0.8 0.2 1; 0.1 0.8 0.2 1; 0.1 0.8 0.2 1"
The 4 values on each line are coordinates for the handles describing the easing curve. I find it easiest to first visualize this with a vector drawing:
Without easing our animation looks mechanical. With easing can simulate momentum and breathe life into it.
Aside: The spec for keySplinesˆ requires these values to be between 0 and 1. Initially we had a bounce effect (below) (with values 0.4 1.6 0.8 0.8) but the effect has an out-of-range value. While this works beautifully in Webkit, the animation breaks in Firefox. (The overshoot and turnback in the resulting Bézier curve are what give the circle its bounce)
Because we have 6 transitions, 3 times that the circle moves to a new position and 3 times that it moves to its current position (the pause), we list 6 keySplines, separated by semicolons. You can use different values for each transition too.
keyTimesˆ set the pace of our keyframes.
These times coordinate with our
values. I wanted the movements to take twice as long as the pauses so I gave about 0.22 units to the movements and about 0.11 units to the pauses. Putting that together with the values it looks like:
With even pacing the circle pauses and moves for equal amounts of time.
With our custom pacing I offset the pausing and moving to give the movement more time to play out. These shorter pauses make the animation feel snappy in the right places.
calcModeˆ attribute tells the animation how to transition between values. The
spline values refers to the cubic Bézier easing method we wrote above.
Looping and ending
We set our
indefinite so that our animation loops.
repeatCount="indefinite" /> </circle>
Finally, we close our
animate element and our
Putting it all together
Putting it all together we get:
<circle fill="#FFFFFF" stroke="#B450FF" stroke-width="6" stroke-miterlimit="10" cx="153" cy="127" r="6"> <animate attributeName="cy" from="127" to="127" begin="0s" dur="4s" values="127;117;117;90;90;127;127" keySplines=" 0.1 0.8 0.2 1; 0.1 0.8 0.2 1; 0.1 0.8 0.2 1; 0.1 0.8 0.2 1; 0.1 0.8 0.2 1; 0.1 0.8 0.2 1" keyTimes=" 0;0.22;0.33;0.55;0.66;0.88;1" calcMode="spline" repeatCount="indefinite" /> </circle>
All elements in the chart use this same method. This technique works for the lines also. With the circles we’re animating the
cy position. With each of the lines we animated the
y2 positions (two separate
Writing our animation by hand ultimately gave us a better understanding of how they work.