Search code examples
svgshadowsnap.svgsvg-filterssvg-animate

Snap.svg inner shadow on animated arc


I just starting working with svgs and Snap.svg library. Following a tutorial I created an arc progress indicator. What I want now is to add an inner shadow to the animated arc. I read the documentation but the filter.shadow command only gives the option for an outer shadow. I did some research on adding filters to regular svgs and I added one to the svg that I'm binding to my Snap object. It does give me the inner shadow, however it ruins the arc animation; only about half of the arc appears even though the inner shadow is present. I changed some of the filter settings trying to fix it but nothing works. I'm thinking that filter only works for static svgs.

Any help would be appreciated. Thanx

UPDATE Here is the code that I'm using.

<svg class="gauge" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
 x="0px" y="0px" viewBox="0 0 400 200" xml:space="preserve">
  <path fill="none" stroke="#D1D2D1" stroke-miterlimit="10" d="M199.7,12.7V18h0.4v-5.3H199.7z M382,200c0-50-20.3-95.4-53.1-128.4
    l8.4-8.4l-0.4-0.4l-8.4,8.4C295.6,38.4,250.1,18,200,18S104.5,38.3,71.5,71.2l-8.3-8.3l-0.4,0.4l8.3,8.3C38.3,104.5,18,149.9,18,200
    h44c0-38,15.4-72.4,40.3-97.4l7.5,7.5l0.4-0.4l-7.5-7.5c24.9-24.8,59.2-40.2,97.1-40.3v10.5h0.5V62c38.1,0.1,72.6,15.6,97.5,40.7
    c-0.1-0.1-0.2-0.2-0.3-0.4l-7.4,7.4l0.4,0.4l7.4-7.4c24.8,25,40.2,59.4,40.2,97.3H382z M102.3,102.6l0.3-0.3L102.3,102.6z"/>
</svg>



<script type="text/javascript">
var canvasSize = 400,
    centre = canvasSize/2,
    radius = canvasSize*0.8/2,
    s = Snap('.gauge'),
    path = "",
    startY = centre-radius,
    arc = s.path(path);

function fill(percent) {
    var endpoint = percent*180;
    Snap.animate(0, endpoint,   function (val) {
        arc.remove();
        var d = val,
          dr = d-180;
          radians = Math.PI*(dr)/180,
          endx = centre + radius*Math.cos(radians),
          endy = centre + radius * Math.sin(radians),
          largeArc = d>180 ? 1 : 0;
          path = "M"+startY+","+centre+" A"+radius+","+radius+" 0 "+largeArc+",1 "+endx+","+endy;
          arc = s.path(path);
          arc.attr({
            stroke: '#fff',
            fill: 'none',
            strokeWidth: 44,
          });
    }, 1500, mina.backout);


}

fill(50/100);
</script>

I added this code to the attribute to get an outer shadow.

arc.attr({
    filter : s.filter(Snap.filter.shadow(1, 1, 2, 'black', 0.5))
})

I was wondering if there's something similar for an inner shadow.

I also tried adding this to the the embedded svg and then adding the attribute to the animated arc thinking it might work like a regular, static svg. It does give me an inner shadow, however only part of the animated arc gets displayed; it gets displayed within a rectangle.

   <defs>
     <filter id="inner-shadow">
      <feGaussianBlur result="offset-blur" stdDeviation="5"></feGaussianBlur>
      <feOffset dx="1" dy="1"></feOffset>
      <feFlood flood-color="black" flood-opacity="1" result="color"></feFlood>
      <feComposite operator="in" in2="offset-blur"></feComposite>
      <feComponentTransfer>
        <feFuncA slope="0.5"></feFuncA>
      </feComponentTransfer>
      <feMerge>
      <feMergeNode></feMergeNode>
      <feMergeNode in="SourceGraphic"></feMergeNode>
      </feMerge>
     </filter>
   </defs>

        arc.attr({
          stroke: '#fff',
          fill: 'none',
          strokeWidth: 44,
          filter: 'url(#inner-shadow)',
        });

Solution

  • You can adjust the filter area that its applied over so that it doesn't get cut off. The Snap code seemed to give an error for me, so I just tried it using the svg filter markup, as the problem seems to be SVG related, rather than Snap.

    So the key bit to change would be..

    <filter id="inner-shadow" y="-30" x="-30" height="400" width="500">
    

    This gives an offset wider range. You can read more on that at filter docs

    jsfiddle