如何让 SVG 笔划显示背景?

How can I make a SVG stroke show a background?

我有一个带有类似渐变背景和前景图标的 SVG。目前,该图标只是一个顶部带有多段线的填充路径,但它可以分成多条路径,其中多段线将是笔画。但是,我想让它们显示背景,而不是 polyline/stroke 有颜色,就好像它在前景上切了一个洞一样。由于背景是渐变色(它实际上是阴影滤镜)而不是纯色,我不能只使用与背景颜色相同的笔触来完成此操作。这就是我想象的结果,如果我可以在多条路径上使用负笔画宽度。

例如,我将使用实际用于(我的个人资料图片)的 SVG 所以,这是我当前的 SVG 代码

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" stroke="black" fill="rgb(95,95,95)" viewBox="-30 -30 276 260" width="276" height="260" stroke-width="4">
  <defs>
    <filter id="shadow" x="-30" y="-30" width="276" height="260">
      <feGaussianBlur in="SourceAlpha" out="blurOut" stdDeviation="32"/>
      <feBlend in="SourceGraphic" in2="blurOut" mode="normal"/>
    </filter>
  </defs>

  <rect x="-30" y="-30" width="276" height="260" fill="#c41313" stroke="none"/>
  <path d="M108 0 L216 54 L216 103 L162 130 L162 152 L135 166 L135 187 L108 200 L82 187 L82 166 L54 152 L54 130 L0 103 L0 54 Z" filter="url(#shadow)"/>
  <g fill="none">
    <polyline points="82 166, 108 179, 135 166"/>
    <polyline points="54 130, 108 157, 162 130"/>
    <polyline points="0 54, 108 108, 216 54"/>
    <polyline points="49 78.5, 108 49, 167 78.5"/>
    <line x1="108" y1="0" x2="108" y2="49"/>
    <line x1="108" y1="200" x2="108" y2="108"/>
  </g>
</svg>
我希望它不是黑色轮廓,而是图标中的一个空隙,例如 this。 当然,对于这个特定的图像,我可以手动制作每个部分 - 由黑色轮廓分隔 - 有自己的路径而没有笔触,重新创建效果,但由于我使用此图像的方式,我需要笔触-width 是可变的,并且具有特定路径不允许这样做,除非有一种方法可以在 Javascript 或 PHP 中自动生成这些路径,但我不知道如何实现它。我能想到的解决这个问题的唯一方法是找到每个部分的中心并增加灰色轮廓以减小间隙(因此最大间隙将是 stroke-width 为 0,最小间隙将具有 stroke-宽度等于每个部分的半径),但对于我的情况,这仍然无效。那么还有哪些选择呢?

使用 CSS mix-blend-mode 似乎可以达到你想要的效果,但不确定如何修复灰色和阴影:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" stroke="black" fill="rgb(95,95,95)" viewBox="-30 -30 276 260" width="276" height="260" stroke-width="4">
  <defs>
    <filter id="shadow" x="-30" y="-30" width="276" height="260">
      <feGaussianBlur in="SourceAlpha" out="blurOut" stdDeviation="32"/>
      <feBlend in="SourceGraphic" in2="blurOut" mode="normal"/>
    </filter>
  </defs>
  
  <rect x="-30" y="-30" width="276" height="260" fill="#c41313" stroke="none"/>
    <g stroke="black" style="mix-blend-mode: color-dodge;">
    <path d="M108 0 L216 54 L216 103 L162 130 L162 152 L135 166 L135 187 L108 200 L82 187 L82 166 L54 152 L54 130 L0 103 L0 54 Z" filter="url(#shadow)"/>
    <g fill="none">
      <polyline points="82 166, 108 179, 135 166"/>
      <polyline points="54 130, 108 157, 162 130"/>
      <polyline points="0 54, 108 108, 216 54"/>
      <polyline points="49 78.5, 108 49, 167 78.5"/>
      <line x1="108" y1="0" x2="108" y2="49"/>
      <line x1="108" y1="200" x2="108" y2="108"/>
    </g>
  </g>
</svg>

您可以使用 green-screen 技术在单个过滤器中执行此操作。您将外部和内部线条设为纯蓝色和绿色,然后使用 ColorMatrix 将它们 select 分成单独的层,然后将它们从原始形状 composite/out 。使用 shape-rendering=crispEdges 很重要,因为如果不这样做,会有 anti-aliasing 个看起来很糟糕的工件。

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-30 -30 276 260" width="276" height="260" stroke-width="4" color-interpolation-filters="sRGB" shape-rendering="crispEdges">
  <defs>
    <filter id="green-screen" x="-30" y="-30" width="276" height="260">
      <feColorMatrix type="matrix" values="0 0 0 0 0  0 2 0 0 -1  0 0 0 0 0  -1 2 -1 0 -1" result="exterior-line"/>
      <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 0   0 0 0 0 0  0 0 2 0 -1  -1 -1 2 0 -1" result="interior-line"/>
      
      <feComposite operator="out" in="SourceGraphic" in2="exterior-line"/>
      <feComposite operator="out"  in2="interior-line" result="masked-shape"/>
      
      <feGaussianBlur in="SourceAlpha" out="blurOut" stdDeviation="32"/>
      <feComposite operator="over" in="masked-shape"/>
    </filter>
  </defs>

  <rect x="-30" y="-30" width="276" height="260" fill="#c41313"/>
  <g filter="url(#green-screen)">
    <path d="M108 0 L216 54 L216 103 L162 130 L162 152 L135 166 L135 187 L108 200 L82 187 L82 166 L54 152 L54 130 L0 103 L0 54 Z" fill="rgb(95,95,95)" stroke="rgb(0,255,0)"/>
    <g fill="none" stroke="rgb(0,0,255)">
      <polyline points="82 166, 108 179, 135 166"/>
      <polyline points="54 130, 108 157, 162 130"/>
      <polyline points="0 54, 108 108, 216 54"/>
      <polyline points="49 78.5, 108 49, 167 78.5"/>
      <line x1="108" y1="0" x2="108" y2="49"/>
      <line x1="108" y1="200" x2="108" y2="108"/>
    </g>
  </g>
</svg>

更传统的方法是使用 <mask>:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 276 260" width="276" height="260">
  <defs>
    <mask id="mask" stroke-width="4" stroke="#000">
      <g transform="translate(30 30)">
        <path id="shapeBG" d="M108 0 L216 54 L216 103 L162 130 L162 152 L135 166 L135 187 L108 200 L82 187 L82 166 L54 152 L54 130 L0 103 L0 54 Z" fill="#fff" />
        <g fill="none">
          <polyline points="82 166, 108 179, 135 166" />
          <polyline points="54 130, 108 157, 162 130" />
          <polyline points="0 54, 108 108, 216 54" />
          <polyline points="49 78.5, 108 49, 167 78.5" />
          <line x1="108" y1="0" x2="108" y2="49" />
          <line x1="108" y1="200" x2="108" y2="108" />
        </g>
      </g>
    </mask>
    <filter id="shadow" x="0" y="0" width="276" height="260">
      <feGaussianBlur in="SourceAlpha" out="blurOut" stdDeviation="32" />
      <feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
    </filter>
  </defs>
  <rect id="bg" x="0" y="0" width="100%" height="100%" fill="#c41313" stroke="none" />
  <g filter="url(#shadow)">
    <rect x="0" y="0" width="100%" height="100%" mask="url(#mask)" fill="rgb(95,95,95)" />
  </g>
</svg>

<svg xmlns="http://www.w3.org/2000/svg" viewBox="-30 -30 276 260" width="276" height="260">
  <g id="mask" stroke-width="4" stroke="#000">
    <path id="shapeBG" d="M108 0 L216 54 L216 103 L162 130 L162 152 L135 166 L135 187 L108 200 L82 187 L82 166 L54 152 L54 130 L0 103 L0 54 Z" fill="#fff" />
    <g fill="none">
      <polyline points="82 166, 108 179, 135 166" />
      <polyline points="54 130, 108 157, 162 130" />
      <polyline points="0 54, 108 108, 216 54" />
      <polyline points="49 78.5, 108 49, 167 78.5" />
      <line x1="108" y1="0" x2="108" y2="49" />
      <line x1="108" y1="200" x2="108" y2="108" />
    </g>
    <text style="font-size:11; font-family:Arial, sans-serif" stroke-width="0" x="0" y="80%" dominant-baseline="middle">Mask: white fills/strokes=opaque; <tspan x="0" dy="12">black fills/strokes=transparent<tspan></text>
</svg>

在右侧,您会看到实际的掩码图形:

  • 白色fills/strokes会变得不透明
  • 黑色fills/strokes会变透明

<mask> 也可用于获得 semi-transparent 笔画,例如将笔画颜色设置为 rgb(128,128,128).

关于软件支持,您还可以通过转换重新对齐图形,以避免负 viewBox 值:一些编辑器对此有疑问。