Search code examples
cssanimationsvgcompositeporter-duff

Animating feComposite SVG filter elements


I have two svg shapes, one on top of the other, and I've applied the feComposite filter on both so that the top shape knocks out part of the bottom shape. The code is as follows and works fine.

   <svg width="100" height="100">
    <defs>
       <filter id="myfilter">
         <feImage xlink:href="#layer1" result="lay1"/>
         <feImage xlink:href="#layer2" result="lay2"/>
         <feComposite operator="out" in="lay1" in2="lay2"/>
       </filter>
    </defs>   
    <g filter="url(#myfilter)">
      <circle id="layer1" cx="50" cy="50" r="40" stroke- 
      width="0" fill="green" />
      <circle id="layer2" class="small" cx="20" cy="60" r="20" stroke- 
      width="0" fill="red" />
    </g>
  </svg>

Now I want to animate the top shape, but when I tried to apply the animation, both shapes animates and I can't figure out why because I've only targeted the second shape with class="small". Here is the css code for the animation.

  @keyframes rock {
    0% {
      transform: rotate(45deg);
    }
    50% {
      transform: rotate(0deg);
    }
    100% {
      transform: rotate(-45deg);
    }
  }

  .small {
    animation-name: rock;
    animation-duration: 5s;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
  }

I am puzzled by this behavior and hope someone can shed some light to this problem. Is it not possible to animate the svg elements individually when filters are applied to them as a group? But that doesn't seem to make sense because the svg elements can be targeted individually.

Here is a link to codepen: https://codepen.io/lanlanonearth/pen/bGbRyVo


Solution

  • Please try this: You put both circles in the <defs> and you <use xlink:href="#layer1". Next you apply the filter to the <use> element.

    <svg width="100" height="100">
      <defs>
      <circle id="layer1" cx="50" cy="50" r="40" stroke-width="0" fill="green" />
      <circle id="layer2" class="small" cx="20" cy="60" r="20" stroke-width="0" fill="red" />
         <filter id="myfilter">
           <feImage xlink:href="#layer1" result="lay1"/>
           <feImage xlink:href="#layer2" result="lay2"/>
           <feComposite operator="out" in="lay1" in2="lay2"/>
         </filter>
      </defs>   
      <use  xlink:href="#layer1" filter="url(#myfilter)"></use>
    </svg>
    

    Check it on codepen