Search code examples
svg

When using feComposite, it causes a displacement of the original element's position


I want to merge an SVG rectangle and a rounded line head into a single entity, so I used feComposite. However, I found that this method causes a displacement of the rectangle and the line, as shown below. I tried to read the MDN documentation, but I still don't know why this happens. Is there another way to achieve my goal? Thanks for your help! And here is my codepen demo: https://codepen.io/cactusxx/pen/GRzJGqv enter image description here

<svg width="350" height="200">
  <g>
    <line id="layer1" x1="100" y1="50" x2="300" y2="50" stroke-width="20" stroke="rgb(0,0,0,0.5)" stroke-linecap="round" />
    <path id="layer2" d="M100,50 L100,25 L75,25 L75,75 L100,75 Z" fill="rgb(0,0,0,0.5)" />
  </g>
</svg>

<svg width="350" height="200">
  <defs>
    <filter id="myfilter1">
      <!-- Use feImage to get copies of each of the layers -->
      <feImage xlink:href="#layer1" result="lay1" />
      <feImage xlink:href="#layer2" result="lay2" />
      <feComposite operator="xor" in="lay2" in2="lay1" result="lay1xor2" />
    </filter>
  </defs>

  <g filter="url(#myfilter1)">
    <line id="layer1" x1="100" y1="50" x2="300" y2="50" stroke-width="20" stroke="rgb(0,0,0,0.5)" stroke-linecap="round" />
    <path id="layer2" d="M100,50 L100,25 L75,25 L75,75 L100,75 Z" fill="rgb(0,0,0,0.5)" />
  </g>

</svg>

Solution

  • Because you're not supplying explicit x,y,width,height on your feImage primitives, it's centering each imported shape in the bounding box of the SourceGraphic (the combined bounding box of the shapes inside the g element you're applying the filter to. This is actually spec-compliant.

    (FWIW This is a weird way to do a filter - although you're applying the filter to a group of shapes, you're actually discarding the SourceGraphic in favor of directly importing those shapes with feImage.)