Animated SVG Doughnut Chart
104px circumference to leave 2 x 3% gap
by moob
HTML
<svg id="donutBase" viewBox="0 0 33.10422816311423 33.10422816311423" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Doughnut</title>
<defs>
<circle id="background-ring" cx="16.5521140816" cy="16.5521140816" r="16.5521140816"></circle>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient">
<stop stop-color="#FB0071" offset="0%"></stop>
<stop stop-color="#5C0873" offset="100%"></stop>
</linearGradient>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient2">
<stop stop-color="#FB5C08" offset="0%"></stop>
<stop stop-color="#FBFB00" offset="100%"></stop>
</linearGradient>
<!-- Rotation of foreground-ring must leave it with start position vertical either at 0 or 180degrees from the upright, otherwise the scale flip on the <use> tag will flip it in the wrong axis-->
<circle id="foreground-ring" cx="16.5521140816" cy="16.5521140816" r="16.5521140816" transform="rotate(93.46153846154 16.5521140816 16.5521140816)"></circle>
<line id="line" x1="16.5521140816" y1="16.5521140816" x2="16.5521140816" y2="33.10422816311423" stroke-width="2" stroke="black" />
</defs>
</svg>
<div id="donutWrapper" class="waiting">
<svg id="chart2" viewBox="-5 -5 43.10422816311423 43.10422816311423">
<g stroke-width="10">
<!-- This scale() transform flips in the vertical axis, so make sure foreground-ring rotates the start point to match . NB 1 deg = 3.46153846154 + 2deg gap (6.92307692308) -->
<use class="backg" xlink:href="#background-ring"></use>
<use class="foreg" xlink:href="#foreground-ring" stroke-dashoffset="40px"></use>
<use class="foreg2" xlink:href="#foreground-ring" stroke-dashoffset="60px" transform="scale(-1,1) translate(-33.10422816311423,0)"></use>
<use class="line"...
CSS
#donutBase {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
svg:not(:root) {
overflow: hidden;
}
svg .backg {
stroke: #ccc;
fill-opacity: 0;
stroke-opacity: 0.4;
}
.waiting svg .foreg {
stroke-dashoffset:100px;
}
.waiting svg .foreg2 {
stroke-dashoffset:100px;
}
svg .foreg {
stroke: url(#linearGradient);
fill-opacity: 0;
stroke-opacity: 1;
stroke-dasharray: 100px 1000px;
transition: stroke-dashoffset 1.5s cubic-bezier(0.25, 0.1, 0.23, 0.97);
}
svg .foreg2 {
stroke: url(#linearGradient2);
mask: url(#ring-mask);
fill-opacity: 0;
stroke-opacity: 1;
stroke-dasharray: 100px 1000px;
transition: stroke-dashoffset 1.5s cubic-bezier(0.25, 0.1, 0.23, 0.97);
}
svg #line {
stroke:#fff;
transition: transform 1.5s cubic-bezier(0.25, 0.1, 0.23, 0.97);
}
#donutWrapper {
width: 50%;
}
div ~ svg {
width: 100%;
}
#chart2 .backg {
stroke: #fff;
}
#chart3, #chart4, #chart5 {
width: 50px;
}
#chart4 .backg {
stroke: #eee;
}
#chart6 {width:100px;}
/* override a gradient:
#linearGradient2 stop {
stop-color:#005C08;
}
#linearGradient2 stop + stop {
stop-color:#00FB00;
} */
JavaScript
(function() {
document.getElementById("donutWrapper").className="";
var foregs = document.querySelectorAll(".foreg"),
x = document.getElementById("x"),
fnc = function() {
for (i = 0; i < foregs.length; ++i) {
var r = (Math.random() * 100);
foregs[i].setAttribute("stroke-dashoffset", r + "px");
console.log(foregs[i]);
if (foregs[i].nextSibling.nextSibling) {
var nx = foregs[i].nextSibling.nextSibling;
var ln = foregs[i].nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling;
var dof = ((100-r));
nx.setAttribute("stroke-dashoffset", dof + "px");
ln.setAttribute("transform", "rotate("+((dof*3.46153846154)+6.92307692308)+" 16.5521140816 16.5521140816)");
console.log(nx);
}
}
};
x.addEventListener("click", fnc, false);
})();