Animating SVG pie chart from 0% to 100% in pure SM

2019-09-09 16:57发布

How does one recreate this SVG pie chart animation in pure SMIL? I'm looking to ditch the complex JS and also be able to control the total duration of the animation:

http://jsfiddle.net/frank_o/gFnw9/19/

So far this is all I got:

http://jsfiddle.net/frank_o/46mH2/ (thanks Ian)

But unfortunately:

  • it's positioned way off the canvas (or wasn't a full circle to begin with)
  • starts at 9 o'clock as opposed to 12 o'clock
  • uses Snap.svg (would rather not depend on any external libs but will if I have to)

HTML:

<svg width="600" height="425">
    <path d="M 100, 100 m -75, 0 a 75,75 0 1,0 150,0 a 75,75 0 1,0 -150,0" fill="none" stroke="black" stroke-width="150" stroke-dasharray="0 600 600 0" stroke-dashoffset="1000">
        <animate attributeType="XML" attributeName="stroke-dashoffset" from="0" to="600" dur="1s" repeatCount="1" fill="freeze"/> 
    </path>
</svg>

JS:

var s = Snap(600,600);

var c = s.circle(150, 150, 80).attr({
    fill: "none",
    stroke: 'red',
    strokeWidth: 161,
    strokeDasharray: "0 600 600 0",
    strokeDashoffset: 1000
});

Snap.animate(0,600, function( value ){ 
       c.attr({ 'strokeDashoffset': value })

},5000 );

UPDATE:

Problem:

enter image description here

Should be:

enter image description here

2条回答
对你真心纯属浪费
2楼-- · 2019-09-09 18:02

you can apply a trasformation on your path like so:

<svg width="600" height="425">
    <path d="M 100, 100 m -75, 0 a 75,75 0 1,0 150,0 a 75,75 0 1,0 -150,0" fill="none" stroke="black" stroke-width="150" stroke-dasharray="0 600 600 0" stroke-dashoffset="1000" transform="translate(75,75) rotate(90,100,100) ">
        <animate attributeType="XML" attributeName="stroke-dashoffset" from="0" to="600" dur="2s" repeatCount="1" fill="freeze"/> 
    </path>
</svg>

http://jsfiddle.net/46mH2/1/

The rotation transformation will make it start at 12o'clock and the translate will offset it half the stroke-width so it is inside the viewbox.
Make sure you apply the transformation in the right order or you won't get the same result.

Update
yes, you can avoid both transformations:

<svg width="600" height="425">
    <path d="M 175, 175 m 0, -75 a 75,75 0 1,0 0,150 a 75,75 0 1,0 0,-150" fill="none" stroke="black" stroke-width="150" stroke-dasharray="0 600 600 0" stroke-dashoffset="1000">
        <animate attributeType="XML" attributeName="stroke-dashoffset" from="0" to="600" dur="2s" repeatCount="1" fill="freeze"/> 
    </path>
</svg>

http://jsfiddle.net/46mH2/3/

set a viewBox on your svg so you can scale the element and still get the whole image visible:

<svg width="600" height="425" viewBox="0 0 600 425">
    <path d="M 100, 100 m -75, 0 a 75,75 0 1,0 150,0 a 75,75 0 1,0 -150,0" fill="none" stroke="black" stroke-width="150" stroke-dasharray="0 600 600 0" stroke-dashoffset="1000" transform="translate(75,75) rotate(90,100,100) ">
        <animate attributeType="XML" attributeName="stroke-dashoffset" from="0" to="600" dur="2s" repeatCount="1" fill="freeze"/> 
    </path>
</svg>

If you're not scaling it proportionally check for the use of preserveAspectRatio to see which one suits you

查看更多
Explosion°爆炸
3楼-- · 2019-09-09 18:04

To solve the problem you posted in the update tag, please take a look into my comparison of 2 solutions: To perfom the animation in solution one, 2 semicircles are rotated. In the second solution as usual the stroke-dashoffset is animated. Both solutions are not pure SMIL but pure Web Animations API which I prefer. With help of the web-animations.min.js polyfill this code runs even in IE 11.

<html>
<head>
  <meta charset="utf-8">
  <title>SVG mit CSS animieren</title>
  <script src="web-animations.min.js"></script>
</head>
<body>
<h1>SVG mit CSS animieren</h1>

<main>
<h2>Kreis in SVG - mit CSS animiert</h2>
<svg width="400px" height="400px" viewBox="-1 -1 2 2" style="margin-left:20px; margin-top:10px; background: lightgray; border-radius: 50%">
  <path id="slice1"      fill="#c32e04"   stroke="none" d="M0,1 a1,1 0 0,1 0,-2 z" />
  <path id="slice2"      fill="#c32e04"   stroke="none" d="M0,1 a1,1 0 0,1 0,-2 z" />
  <path id="slice_cover" fill="lightgray" stroke="none" d="M0,1 a1,1 0 0,1 0,-2 z" />
  <script>
     const duration=5000;
     var anim11, anim12;
     var slice1=document.getElementById("slice1");
     var slice2=document.getElementById("slice2");
     function slice2_ontop() { slice2.parentNode.appendChild(slice2); }
     anim11=slice1.animate([
       {transform: 'rotate(0deg)'},
       {transform: 'rotate(180deg)'}
     ], { duration: duration/2, iterations: 1, fill: "forwards"});
     anim11.onfinish=slice2_ontop;
     anim12=slice2.animate([
       {transform: 'rotate(0deg)'},
       {transform: 'rotate(360deg)'}
     ], { duration: duration, iterations: 1, fill: "forwards"});
  </script>
</svg> 
<svg width="400px" height="400px" viewBox="-1 -1 2 2" style="margin-left:20px; background: lightgray; border-radius: 50%; transform: rotate(-90deg)">
  <circle id="circle" cx="0" cy="0" r="0.5" fill="none" stroke="#c32e04" stroke-width="1px" style="stroke-dasharray: 3.1416 3.1416; stroke-dashoffset: 3.1416"/>
  <script>
     var anim2;
     anim2=document.getElementById("circle").animate([
       {strokeDashoffset: '3.1416px'},
       {strokeDashoffset: '0px'}
     ], { duration: duration, iterations: 1, fill: 'forwards'});
  </script>
</svg> 

</main>
</body>
</html>
查看更多
登录 后发表回答