37.SVG遮罩


SVG 遮罩特性使得对 SVG 形状使用蒙版成为可能。遮罩确定 SVG 形状的哪些部分是可见的,以及具有什么透明度。你可以将 SVG 遮罩视作剪裁路径的更高级版本。

遮罩示例

下面是一个简单的遮罩示例:

<defs>
  <mask id="mask1" x="0" y="0" width="100" height="100" >
    <rect x="0" y="0" width="100" height="50"
        style="stroke:none; fill: #ffffff"/>
  </mask>
</defs>


<rect x="1" y="1" width="100" height="100"
    style="stroke: none; fill: #0000ff; mask: url(#mask1)"/>

这个例子定义了 ID 为mask1的遮罩,<mask>元素内是一个<rect>元素,正是这个<rect>元素定义了遮罩的形状。

例子中还定义了一个使用遮罩的<rect>元素。其通过 CSS 属性mask引用遮罩。

最终图片如下:

<rect x="1" y="1" width="100" height="100" style="stroke: none; fill: #0000ff; mask: url(#mask1)"></rect>
<rect x="1" y="1" width="100" height="100" style="stroke: #000000; fill: none;"></rect>

注意要显示的矩形是 100 像素高,但垂直方向上只有前 50 像素可见。这是因为遮罩矩形只有 50 像素高。矩形仅在有遮罩矩形覆盖的部分中可见。

黑色轮廓矩形是没有遮罩的矩形的大小。

其它形状作为遮罩

你可以使用任何 SVG 形状作为遮罩。下面是使用圆形作为遮罩的例子:

<svg>
  <defs>
    <mask id="mask2" x="0" y="0" width="100" height="100" >
      <circle cx="25" cy="25" r="25" style="stroke:none; fill: #ffffff"/>
    </mask>
  </defs>

  <rect x="1" y="1" width="100" height="100"
    style="stroke: none; fill: #0000ff; mask: url(#mask2)"/>

</svg>

最终图片如下:

<rect x="1" y="1" width="100" height="100" style="stroke: none; fill: #0000ff; mask: url(#mask2)"></rect>
<rect x="1" y="1" width="100" height="100" style="stroke: #000000; fill: none;"></rect>

再次注意,引用遮罩的矩形只在遮罩圆形区域可见。

遮罩形状颜色定义其不透明度

到目前为止,遮罩形状(圆形或矩形)的填充颜色都设置为#ffffff。遮罩形状的颜色定义了使用遮罩形状的不透明度。遮罩形状的颜色越接近#ffffff(白色),使用遮罩的形状越不透明。遮罩形状的颜色越接近#000000(黑色),使用遮罩的形状越透明。

下面的例子中,遮罩又两个不同颜色的矩形组成(#ffffff#666666)。遮罩在单个矩形上使用,因此你可以看到形状在两个不同颜色遮罩形状下的效果。

<defs>
  <mask id="mask3" x="0" y="0" width="100" height="100" >

    <rect x="0" y="0"  width="100" height="50"
          style="stroke:none; fill: #ffffff"/>

    <rect x="0" y="50" width="100" height="50"
          style="stroke:none; fill: #666666"/>
  </mask>
</defs>

<text x="10" y="55" style="stroke: none; fill: #000000;">
    This text is under the rectangle
</text>

<rect x="1" y="1" width="100" height="100"
    style="stroke: none; fill: #0000ff; mask: url(#mask3)"/>

这个例子的矩形下方有一个文本,其仅可通过矩形遮罩是半透明的部分可见。

最终图片如下:

<text x="10" y="55" style="stroke: none; fill: #000000;">This text is under the rectangle</text>
<rect x="1" y="1" width="100" height="100" style="stroke: none; fill: #0000ff; mask: url(#mask3)"></rect>

遮罩中使用渐变

如果对你用作遮罩的形状应用渐变,则可以实现应用遮罩的形状的渐变透明度。

下面例子定义了一个渐变,一个使用渐变的遮罩,一个使用遮罩的矩形以及矩形下方的文本,因此你可以看到随着遮罩渐变的透明度的变化:

<defs>
    <linearGradient id="gradient1"
                    x1="0%"   y1="0%"
                    x2="100%" y2="0%"
                    spreadMethod="pad">
        <stop offset="0%"   stop-color="#ffffff" stop-opacity="1"/>
        <stop offset="100%" stop-color="#000000" stop-opacity="1"/>
    </linearGradient>

    <mask id="mask4" x="0" y="0" width="200" height="100" >
        <rect x="0" y="0"  width="200" height="100"
            style="stroke:none; fill: url(#gradient1)"/>
    </mask>
</defs>

<text x="10" y="55" style="stroke: none; fill: #000000;">
    This text is under the rectangle
</text>
<rect x="1" y="1" width="200" height="100"
    style="stroke: none; fill: #0000ff; mask: url(#mask4)"/>

最终图片如下:

    <mask id="mask4" x="0" y="0" width="200" height="100">
        <rect x="0" y="0" width="200" height="100" style="stroke:none; fill: url(#gradient1)"></rect>
    </mask>
</defs>

<text x="10" y="55" style="stroke: none; fill: #000000;">This text is under the rectangle</text>
<rect x="1" y="1" width="200" height="100" style="stroke: none; fill: #0000ff; mask: url(#mask4)"></rect>

渐变遮罩可以与其它效果(如填充图案)组合。下面例子中的可见矩形同时使用了填充图案和渐变遮罩:

<defs>

  <linearGradient id="gradient2"
                x1="0%"   y1="0%"
                x2="100%" y2="0%"
                spreadMethod="pad">
    <stop offset="0%"   stop-color="#ffffff" stop-opacity="1"/>
    <stop offset="100%" stop-color="#000000" stop-opacity="1"/>
  </linearGradient>


  <pattern id="pattern2"
         x="10" y="10" width="20" height="20"
         patternUnits="userSpaceOnUse" >

    <circle cx="10" cy="10" r="10" style="stroke: none; fill: #0000ff; " />

  </pattern>

  <mask id="mask6" x="0" y="0" width="200" height="100" >
      <rect x="0" y="0"  width="200" height="100"
          style="stroke:none; fill: url(#gradient2)"/>
  </mask>
</defs>

<text x="10" y="55" style="stroke: none; fill: #000000;">
    This text is under the rectangle
</text>
<rect x="1" y="1" width="200" height="100"
      style="stroke: none; fill: url(#pattern2); mask: url(#mask6)"/>

注意要显示的矩形如何从其 CSS 属性fill引用填充图案,并从其 CSS 属性mask引用遮罩。

最终图片如下:

    <linearGradient id="gradient2" x1="0%" y1="0%" x2="100%" y2="0%" spreadMethod="pad">
        <stop offset="0%" stop-color="#ffffff" stop-opacity="1"></stop>
        <stop offset="100%" stop-color="#000000" stop-opacity="1"></stop>
    </linearGradient>


    <pattern id="pattern2" x="10" y="10" width="20" height="20" patternUnits="userSpaceOnUse">

        <circle cx="10" cy="10" r="10" style="stroke: none; fill: #0000ff; "></circle>

    </pattern>

    <mask id="mask6" x="0" y="0" width="200" height="100">
        <rect x="0" y="0" width="200" height="100" style="stroke:none; fill: url(#gradient2)"></rect>
    </mask>
</defs>

<text x="10" y="55" style="stroke: none; fill: #000000;">This text is under the rectangle</text>
<rect x="1" y="1" width="200" height="100" style="stroke: none; fill: url(#pattern2); mask: url(#mask6)"></rect>

遮罩中使用填充图案

你还可以在遮罩中使用填充图案,从而使遮罩成为填充图案的形状。示例如下:

<defs>
  <pattern id="pattern1"
         x="10" y="10" width="20" height="20"
         patternUnits="userSpaceOnUse" >

      <circle cx="10" cy="10" r="10" style="stroke: none; fill: #999999" />
  </pattern>

  <mask id="mask5" x="0" y="0" width="200" height="100" >
    <rect x="0" y="0"  width="200" height="100"
        style="stroke:none; fill: url(#pattern1)"/>
  </mask>
</defs>

<text x="10" y="55" style="stroke: none; fill: #000000;">
    This text is under the rectangle
</text>
<rect x="1" y="1" width="200" height="100"
    style="stroke: none; fill: #0000ff; mask: url(#mask5)"/>

最终图片如下。注意,现在矩形中填充图案部分是半透明的,其它部分是完全透明的。

    <pattern id="pattern1" x="10" y="10" width="20" height="20" patternUnits="userSpaceOnUse">

        <circle cx="10" cy="10" r="10" style="stroke: none; fill: #999999"></circle>

    </pattern>

    <mask id="mask5" x="0" y="0" width="200" height="100">
        <rect x="0" y="0" width="200" height="100" style="stroke:none; fill: url(#pattern1)"></rect>
    </mask>
</defs>

<text x="10" y="55" style="stroke: none; fill: #000000;">This text is under the rectangle</text>
<rect x="1" y="1" width="200" height="100" style="stroke: none; fill: #0000ff; mask: url(#mask5)"></rect>

mask vs clipPath

mask 和 clipPath 在用法和用途上非常相似,但 mask 是作为 clipPath 更高级的能力出现的。但有一点区别是 clipPath 不处理 alpha,它只会剪切标签中形状边界内的内容。我们来通过代码来看下:

<svg viewBox="0 5 100 40" class="clip-mask">
  <linearGradient id="gradient" x2="0" y2="1">
    <stop offset="0%" stop-color="black" />
    <stop offset="100%" stop-color="white" />
  </linearGradient>
  <clipPath id="clip">
    <rect x="0" y="15" width="100" height="20" fill="url(#gradient)" />
  </clipPath>
  <mask id="mask">
    <rect x="0" y="15" width="100" height="20" fill="url(#gradient)" />
  </mask>

  <circle cx="25" cy="25" r="15" fill="pink" clip-path="url(#clip)" />
  <circle cx="75" cy="25" r="15" fill="pink" mask="url(#mask)" />
</svg>

这段代码的运行结果如图所示:

之所以会有不同的结果,这是因为 clipPath 只关注形状的裁切,不关心 alpha 通道。更多信息参考 06. Clip-path & Mask

最后更新于