After not solving the problem of an animated circle chart satisfyingly, I couldn’t sleep last night. It was somehow clear to me that this has to be solved using plain CSS and SVG. And I had the feeling that it could be done using only those two techniques. I was lying in bed thinking about possible solutions until I first realized how the problem could be at least simplified. I walked into my living room to grab my laptop and started hacking.
Part 1: Math is hard
First of all let me explain how you can change the length of the stroke of an SVG circle which is filled. You need to modify the
stroke-dasharray attribute. The
stroke-dasharray attribute usually controls the pattern of dashes and gaps used to stroke paths, but if you set it to a dash length which represents the percentage you want to fill and a gap length which represents the full circumference of the circle, you can use it to display a partially filled circle.
See the Pen Part 1: Math is hard (Pure CSS animated SVG Circle Chart) by Markus Oberlehner (@maoberlehner) on CodePen.
In the example above you can see the math involved for calculating the value for the
stroke-dasharray attribute to represent a circle which is filled to 25%.
As you might remember from school, the formula to calculate the circumference of a circle reads as:
2 * π * Radius. Knowing the circumference of our circle we can calculate the length that needs to be filled to represent a certain percentage value. The formula is
(Circumference / 100) * Percentage to fill.
Although this is not exactly NASA level math, it makes understanding the code and changing the percentage value an unnecessary hard task to do.
Back to my nightly adventures. The idea – which suddenly crossed my mind when I was lying in bed – to fix this circumference problem is so simple, I guess some of you already figured it out while reading the last paragraph: we have to change the radius of the circle so that the formula
(Circumference / 100) * Percentage to fill equals
Percentage to fill.
The ultimate formula to rule all complicated SVG circle math problems once and for all reads as follows:
100 / (π * 2).
See the Pen Part 1.2: Make math easy (Pure CSS animated SVG Circle Chart) by Markus Oberlehner (@maoberlehner) on CodePen.
After recalculating all the values of our circle to follow the magic number which is the result of our beautiful formula, setting the percentage value is a much easier task to do. We now can replace
stroke-dasharray=”dash-length,gap-length” with values like
25 for the dash length and
100 for the gap length if we want to display a circle which is 25% filled.
Part 2: Animating to an inline value
Spurred by the victory over a primary school math problem, I did the unthinkable and actually read the documentation of the CSS “animation” property. And there it was waiting for me, this magic property called
See the Pen Part 2: Animating to an inline value (Pure CSS animated SVG Circle Chart) by Markus Oberlehner (@maoberlehner) on CodePen.
reverse plays the animation backwards. Instead of ending the animation with the value defined in the CSS keyframe animation it begins with this value. The
stroke-dasharray in the keyframe animation is set to
0 100 which means the stroke is not filled at all. This is the starting point and the animation ends with the value which is set inline in the SVG, which is
25, representing 25%.
Part 3: Let’s get crazy
If you can’t outright avoid displaying values lower than 0%, there is no perfect and elegant solution to this problem. Passing a negative number to our circle chart module would result in
stroke-dasharray="-25,100" which does not work. The solution I came up with is, that you have to pass an additional parameter to the module to display negative values. The parameters passed to the template might look something like this:
negative parameter is set to
true a modifier class, which mirrors the circle, is applied to the circle element with the effect of filling the circle in the opposite direction, representing a negative value.
Part 4: Internet Explorer strikes back
At this point I thought I was done, but as one of my colleagues noticed, this approach doesn’t quite work in Internet Explorer and Edge. Looking up the
transform property at caniuse.com quickly revealed whats the problem: Internet Explorer and Edge do not support CSS transforms on SVG elements. But they support using the transform attribute directly in the SVG itself.
Because setting the transform origin when using the
transform attribute is not possible, rotating and mirroring an SVG element is a little bit trickier that way:
transform="rotate(-90 16.91549431 16.91549431)". We have to use the second and third parameter of
rotate to rotate the element using its center as origin. Because
scale does not support setting the transform origin, we have to use
matrix instead, to mirror the SVG element. The transformation attribute to display negative values, looks like the following:
transform="rotate(-90 16.91549431 16.91549431) matrix(-1, 0, 0, 1, 33.83098862, 0)".
Wrapping it up
It was a lot of fun and a great learning experience coming up with a CSS only solution for animating an SVG circle chart. The following pen includes all the features and I tried to add some useful comments to make it easier to understand how all the CSS properties work together.