Search code examples
htmlcssimagesvgvector-graphics

SVG Clip Path Behavior under Translate Transforms


I am seeking the wisdom of the StackOverflow community to clarify my understanding of the clip-path feature in the SVG 1.1 specifications, particularly under multiple translate transformations.

I have come up with the following SVG segment that delivers results contrary to my understanding of the SVG spec (across all browsers):

<svg xmlns="http://www.w3.org/2000/svg" width="500" height="300" viewBox="0 0 500 300">
  <g id="wave_0">
    <rect width="500" height="300" style="stroke:#000;fill:white"></rect>
    <g transform="translate(140.5,20.5)">
      <g id="expcb_0">
        <path style="stroke:#f00;fill:none" d="M 0 -2 L 200 -2 L 200 200 L 0 200 Z"></path>
      </g>
      <g id="pwtl_0" clip-path="polygon(0 -2, 200 -2, 200 200, 0 200)">
        <g transform="translate(0,55)">
          <path style="stroke:#000;stroke-width:1px" d="M 80 0 L 280 0"></path>
        </g>
      </g>
    </g>
  </g>
</svg>

All SVG elements except the 500x300 rect background are rendered under a translate transform starting at (140.5,20.5).

The first element group under it is a rectangular region in red with a certain set of coordinates that also matches the clip-path polygon shape I am setting up n the second group.

Under the second group is another element group that has a single horizontal line.

My expectation is that the masking polygon will be the same as the previously specified red rectangle, and the horizontal line will be clipped to be within the red rectangle, but that is unfortunately not the case.

Rendering of above SVG in Chrome 121.0.6164.0 Canary

As an additional experiment, I added an 'invisible' sibling to this line, and now I do get some clipping behavior, but the clipping coordinates do not match my expectations.

<svg xmlns="http://www.w3.org/2000/svg" width="500" height="300" viewBox="0 0 500 300">
  <g id="wave_0">
    <rect width="500" height="300" style="stroke:#000;fill:white"></rect>
    <g transform="translate(140.5,20.5)">
      <g id="expcb_0">
        <path style="stroke:#f00;fill:none" d="M 0 -2 L 200 -2 L 200 200 L 0 200 Z"></path>
      </g>
      <g id="pwtl_0" clip-path="polygon(0 -2, 200 -2, 200 200, 0 200)">
        <g transform="translate(0,55)">
          <path style="stroke:#000;stroke-width:1px" d="M 80 0 L 280 0"></path>
        </g>
        <g transform="translate(0,65)">
          <path style="stroke:#000;stroke-width:1px;stroke-opacity:0" d="M -20 0 L 280 0"></path>
        </g>
      </g>
    </g>
  </g>
</svg>

Rendering of above SVG in Chrome 121.0.6164.0 Canary

The only time that the clipping works as expected is if the black horizontal line to be clipped starts at the left boundary of the polygon specified in the clip-path.

How exactly should I be framing the clip-path polygon's coordinates if I want the black horizontal line in the two SVGs above to be cut off at the red vertical? I can't change the two translate transform specifications, unfortunately. So, I am in search of a reliable solution that can work with the translations in place.

Edit: Interestingly, if I 'convert' the in-line clip-path specification in CSS style to a SVG-style 'url' / clipPath scheme, the rendering is as expected (for the first SVG without the invisible sibling):

<svg xmlns="http://www.w3.org/2000/svg" width="500" height="300" viewBox="0 0 500 300">
  <defs>
    <clipPath id="D0_PWTL">
      <polygon points="0,-2,200,-2,200,200,0,200"></polygon>
    </clipPath>
  </defs>
  <g id="wave_0">
    <rect width="500" height="300" style="stroke:#000;fill:white"></rect>
    <g transform="translate(140.5,20.5)">
      <g id="expcb_0">
        <path style="stroke:#f00;fill:none" d="M 0 -2 L 200 -2 L 200 200 L 0 200 Z"></path>
      </g>
      <!-- <g id="pwtl_0" clip-path="polygon(0 -2, 200 -2, 200 200, 0 200)"> -->
      <g id="pwtl_0" clip-path="url(#D0_PWTL)">
        <g transform="translate(0,55)">
          <path style="stroke:#000;stroke-width:1px" d="M 80 0 L 280 0"></path>
        </g>
      </g>
    </g>
  </g>
</svg>

I am still looking into how to get CSS-style clip-path specifications to work reliably.


Solution

    • CSS clip-paths work on the geometry box of the object being clipped i.e. you get the bounding box of the shape and then the clip-path is relative to that.

    • SVG clip-paths with userSpaceOnUse units (the default) work with the same co-ordinate system as the element referencing the clipPath element.

    Different co-ordinate systems, different results.