Search code examples
javascriptcssanimationsvgsmil

How to animate multiple SVG paths in a pie/donut chart in sequence with SMIL/CSS


I have an svg group that contains a series of paths which represent different areas of a pie chart these paths are convered by a circle tag and the graph appears as a donut chart.

<svg width="71" height="70" xmlns="http://www.w3.org/2000/svg">
    <g class="graph-arc" transform="translate(1,1)">
        <path stroke="#3f88c5" fill="#3f88c5" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(225, 34.5, 34.5)"></path>
        <path stroke="#3f88c5" fill="#3f88c5" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(238.5, 34.5, 34.5)"></path>
        <path stroke="#3A8DC3" fill="#3A8DC3" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(252, 34.5, 34.5)"></path>
        <path stroke="#3593BF" fill="#3593BF" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(265.5, 34.5, 34.5)"></path>
        <path stroke="#3398BE" fill="#3398BE" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(279, 34.5, 34.5)"></path>
        <path stroke="#3398BE" fill="#3398BE" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(292.5, 34.5, 34.5)"></path>
        <path stroke="#359DBB" fill="#359DBB" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(306, 34.5, 34.5)"></path>
        <path stroke="#36A3B9" fill="#36A3B9" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(319.5, 34.5, 34.5)"></path>
        <path stroke="#36A3B9" fill="#36A3B9" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(333, 34.5, 34.5)"></path>
        <path stroke="#38A8B6" fill="#38A8B6" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(346.5, 34.5, 34.5)"></path>
        <path stroke="#39ADB4" fill="#39ADB4" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(360, 34.5, 34.5)"></path>
        <path stroke="#39ADB4" fill="#39ADB4" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(373.5, 34.5, 34.5)"></path>
        <path stroke="#3BB3B2" fill="#3BB3B2" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(387, 34.5, 34.5)"></path>
        <path stroke="#3DB8AE" fill="#3DB8AE" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(400.5, 34.5, 34.5)"></path>
        <path stroke="#3DB8AE" fill="#3DB8AE" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(414, 34.5, 34.5)"></path>
        <path stroke="#3EBDAD" fill="#3EBDAD" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(427.5, 34.5, 34.5)"></path>
        <path stroke="#40C3AA" fill="#40C3AA" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(441, 34.5, 34.5)"></path>
        <path stroke="#40C3AA" fill="#40C3AA" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(454.5, 34.5, 34.5)"></path>
        <path stroke="#00c8a8" fill="#00c8a8" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(468, 34.5, 34.5)"></path>
        <path stroke="#00c8a8" fill="#00c8a8" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 42.55386505302874, 0.9532377462801749 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(481.5, 34.5, 34.5)"></path>
        <path stroke="#e5e5e5" fill="#e5e5e5" d="M 34.5,34.5 L34.5,0 A34.5,34.5 0 0,1 34.5, 0 z" xmlns="http://www.w3.org/2000/svg" transform="rotate(495, 34.5, 34.5)"></path>
    </g>
    <circle r="25.875" cx="35.5" cy="35.5" fill="#fff"></circle> . 
</svg>

I would like the overall effect to be like this: https://codepen.io/seanstopnik/pen/GqHng

There are many examples using stroke-dasharray for lines but I haven't been able to find anything for animating multiple paths! What is the best way to do this?


Solution

  • As Temani Afif commented using multiple path isn't the good way. This is how I would do it:

    To calculate the path length (155.53) I've used JS & the method getTotalLength. I've calculated the path as an arc with a radius of 33 units starting at 3*Math.PI/4 and ending at Math.PI/4.

    The animation happens on mouse over.

    svg{border:1px solid;width:90vh}
    
    #thePath{
      stroke-dasharray : 155.53px;
      stroke-dashoffset : 155.53px;
      transition: stroke-dashoffset .5s;
    }
    
    svg:hover #thePath{
      stroke-dashoffset : 0px;
    }
    <svg viewBox ="0 0 71 70" id="svg">
    <defs>
      <linearGradient id="lg">
       <stop offset="0%" stop-color="#3f88c5"></stop>
       <stop offset="100%" stop-color="#00c8a8"></stop>
      </linearGradient>
     </defs>
      
      <path id="thePath" d="M12.165476220843935,58.33452377915607A33,33,0 1 1 58.834523779156065,58.334523779156065" fill="none" stroke="url(#lg)" stroke-width="4" />
    </svg>

    And this is the code where I'm using JS to calculate all this:

    let R = 33;// the radius of the arc for the path
    // the starting point of the path
    let x = 35.5 + R * Math.cos(3 * Math.PI / 4);
    let y = 35 + R * Math.sin(3 * Math.PI / 4);
    // the end point of the path
    let _x = 35.5 + R * Math.sin(Math.PI / 4);
    let _y = 35 + R * Math.sin(Math.PI / 4);
    // the value of the d attribute of the path
    let d = `M${x},${y}A${R},${R},0 1 1 ${_x},${_y}`;
    thePath.setAttributeNS(null, "d", d);
    
    let pathLength = thePath.getTotalLength();
    
    thePath.setAttribute(
      "style",
      `stroke-dasharray : ${pathLength}px; stroke-dashoffset : ${pathLength}px;`
    );
    svg{border:1px solid;width:90vh;}
    
    #thePath{ 
      transition: stroke-dashoffset .5s;
    }
    
    svg:hover #thePath{
      stroke-dashoffset : 0px!important;
    }
    <svg viewBox ="0 0 71 70" id="svg">
    <defs>
      <linearGradient id="lg">
       <stop offset="0%" stop-color="#3f88c5"></stop>
       <stop offset="100%" stop-color="#00c8a8"></stop>
      </linearGradient>
     </defs>
      
      <path id="thePath" fill="none" stroke="url(#lg)" stroke-width="4" />
    </svg>