Search code examples
htmlimagesvgmask

Is it possible to create a mask in an SVG file?


I created this SVG, which displays correctly in a web browser.

<svg width="256" height="256">
  <defs>
<g id="maskPath">
  <polygon points="101 50 135 50 135 29 101 29" fill="black" transform="rotate(10, 101, 24)"/>
  <polygon points="121 50 155 50 155 29 121 29" fill="black" transform=" rotate(-10, 155, 24)"/>
</g>
<g id="symmetry" mask="url">
  <polygon points="128 124 116 100 124 100 124 56 132 56 132 100 140 100 128 124"/>
  <polygon points="96 29 160 29 160 24 96 24"/>
  <polygon points="96 24 120 24 120 19 96 19" transform="rotate(100, 96, 24)"/>
  <polygon points="136 24 160 24 160 19 136 19" transform="rotate(-100, 160, 24)"/>
</g>
  </defs>
  <mask id="mask">
<rect width="256" height="256" fill="white"/>
<use href="#maskPath" transform="rotate (0, 128, 128)"/>
<use href="#maskPath" transform="rotate (120, 128, 128)"/>
<use href="#maskPath" transform="rotate (-120, 128, 128)"/>
  </mask>
  <use href="#symmetry" transform="rotate (0, 128, 128)"/>
  <use href="#symmetry" transform="rotate (120, 128, 128)"/>
  <use href="#symmetry" transform="rotate (240, 128, 128)"/>
  <circle cx="128" cy="128" r="56" stroke="black" stroke-width="12" fill-opacity="0"/>
  <circle cx="128" cy="128" r="88" stroke="black" stroke-width="5" fill-opacity="0" mask="url(#mask)"/>
</svg>

Image showing the SVG correctly

However if I change the extension to .svg and open it in an SVG viewer, it displays incorrectly. It looks like everything in the <defs> and <mask> is missing.

Image showing the SVG incorrectly

Are the <mask> and <defs> HTML-only tags? The result is similar in other SVG viewers. Is there another way I can cut sections of the circle out? The green in this image is the mask I want to apply.

Correct mask

The background must be transparent, so I can't just use white to create a fake mask.

Edit: Replacing href with xlink:href makes the arrows appear, but the outer circle is still missing. (In Inkscape the circle is there but it isn't masked.)

Different broken SVG


Solution

  • I've removed the mask altogether and drawn the circle with a stroke-dasharray and rotated it so the gaps are in the right places.

    This is much more efficient as there's no raster mask operation.

    <svg width="256" height="256">
      <defs>
    <g id="symmetry">
      <polygon points="128 124 116 100 124 100 124 56 132 56 132 100 140 100 128 124"/>
      <polygon points="96 29 160 29 160 24 96 24"/>
      <polygon points="96 24 120 24 120 19 96 19" transform="rotate(100, 96, 24)"/>
      <polygon points="136 24 160 24 160 19 136 19" transform="rotate(-100, 160, 24)"/>
    </g>
      </defs>
      <use href="#symmetry" transform="rotate (0, 128, 128)"/>
      <use href="#symmetry" transform="rotate (120, 128, 128)"/>
      <use href="#symmetry" transform="rotate (240, 128, 128)"/>
      <circle cx="128" cy="128" r="56" stroke="black" stroke-width="12" fill="none"/>
      <circle cx="128" cy="128" r="88" stroke="black" stroke-width="5" fill="none" stroke-dasharray="118.3 66" transform="rotate(51.4, 128, 128)"/>
    </svg>

    Exact values courtesy of Paul LeBeau

    There are 3 key numbers here, the stroke-dasharray values that must add to 2 x PI / 3 (because of the 3 fold symmetry) and the first rotate value. You can play with the numbers till you get a perfect result but the values above are pretty good.