Patrick Dark
Patrick Dark

Reputation: 2259

Why is my SVG path unexpectedly clipped in Chrome, Edge, and Firefox?

Consider the following SVG image code:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
    <title>Clipped Path Problem Demo</title>
    <g id="group:image">
        <defs>
            <mask id="mask">
                <rect id="rectangle:inset" x="250" y="134" width="500" height="732" fill="white"/>
            </mask>
        </defs>
        <g id="group:curves" mask="url('#mask')">
            <!-- <rect id="rectangle:filler.shape" width="1" height="1" fill="transparent"/> -->
            <path id="path:curves" d="
                M 441 327
                c -85 88 -85 175 -85 175
                c 0 88 85 175 85 175
                s 90 103 90 103
                c 85 88 85 175 85 175" fill="transparent" stroke="black" stroke-width="73"/>
        </g>
    </g>
    <g id="group:guides">
        <rect id="rectangle:guide.area:inset" x="250" y="134" width="500" height="732" fill="transparent" stroke="black" stroke-dasharray="20 10" stroke-width="1"/>
    </g>
</svg>

(CodePen: https://codepen.io/patrickdark/full/zPEVVg/. (I added a height="500px" attribute to the pen to make it more likely to fit on screen.))

The left side of the curved path is clipped to a vertical line even though the mask is well over the size needed to contain that portion of the image.

If I insert a shape of any size with non-zero dimensions into the masked g element—such as the 1×1 transparent rectangle in the XML comment—the clipped edge is magically unclipped.

I would think this is a bug except that it’s happening in Chrome (62), Edge (41), and Firefox Developer Edition (58 Beta 4). What I would like to know is why this seemingly nonsensical clipping is occurring.

(Note: Clipping at the bottom of the curved path is intentional.)

Upvotes: 3

Views: 1444

Answers (1)

ccprog
ccprog

Reputation: 21811

Since a mask works on color and alpha channel values (a potentially costly operation), it doesn't stretch to infinity, but rendering is clipped of by default at the object bounding box of the masked element plus 10% in every direction (defined by the attributes x, y, width, height in bounding box units).

The object bounding box although is defined not to include stroke widths. You are surpassing these limits and need to make adjustments at least to the x and width attribute of the mask.

Since this seems to be not intuitive, the general algorithm for applying a mask goes like this:

  1. Identify the region the mask is applied to. This is always the rectangle given by the x, y, width, height attributes of the <mask> element. If, as is the default, the maskUnits attribute is given as objectBoundingBox, the coordinates are determined by the object bounding box of the element the mask is applied to. The contents of the mask are not taken into account. 0 and 1 in that coordinate system are the minimum and maximum of the joined bounding box of the masked element and all its descendants.
  2. Superimpose the content of the mask element with the element it is applied to. As a default, the coordinate system used for placing these elements is given as maskContentUnits="userSpaceOnUse", which means the same coordinate system the masked element is drawn into.
  3. For every point within the region defined above, multiply the opacity of the masked content with the luminance of the masking content to compute a resulting opacity of the masked element.
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
        <title>Clipped Path Problem Resoved</title>
        <g id="group:image">
            <defs>
                <mask id="mask" x="-20%" width="140%">
                    <rect id="rectangle:inset" x="250" y="134" width="500" height="732" fill="white"/>
                </mask>
            </defs>
            <g id="group:curves"mask="url('#mask')">
                <!-- <rect id="rectangle:filler.shape" width="1" height="1" fill="transparent"/> -->
                <path id="path:curves" d="
                    M 441 327
                    c -85 88 -85 175 -85 175
                    c 0 88 85 175 85 175
                    s 90 103 90 103
                    c 85 88 85 175 85 175" fill="transparent" stroke="black" stroke-width="73"/>
            </g>
        </g>
        <g id="group:guides">
            <rect id="rectangle:guide.area:inset" x="250" y="134" width="500" height="732" fill="transparent" stroke="black" stroke-dasharray="20 10" stroke-width="1"/>
        </g>
    </svg>

Upvotes: 3

Related Questions