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);
})();