【问题标题】:How can I merge two shapes in svg?如何在 svg 中合并两个形状?
【发布时间】:2020-07-16 23:13:45
【问题描述】:

我有两种形状:圆形和矩形。想把它们转换成一个数字。有什么方法可以在 SVG 代码中做到这一点?

 <svg width="400" height="400">
     <defs>
    <g id="shape" fill="none" stroke="red">
      <rect x="40" y="50" width="40" height="70" />
      <circle cx="50" cy="50" r="50" />
    </g>
  </defs>

  <use xlink:href="#shape" x="50" y="50"  />
  <use xlink:href="#shape" x="200" y="50" />

</svg>

像这样:

【问题讨论】:

  • 我想对这些形状应用阴影滤镜,但我只希望外边框而不是内边框。我希望它是一个完整的形状,但绘制为两个 svg 部分。
  • 使用AI(compound path -> make),会在svg export中制作path属性
  • Artem,没办法用AI,需要JavaScript/HTML代码

标签: html svg


【解决方案1】:

对于任何寻找如何将两个轮廓形状组合成一个轮廓形状(而不是在组合形状上放置阴影)的实际问题的答案的人,这里是一个可能的解决方案:

<svg width="400" height="400">
    <defs>
        <rect id="canvas" width="100%" height="100%" fill="white" />
        <rect id="shape1" x="40" y="50" width="40" height="70" />
        <circle id="shape2" cx="50" cy="50" r="50" />
        <mask id="shape1-cutout">
            <use href="#canvas"  />
            <use href="#shape1"  />
        </mask>
        <mask id="shape2-cutout">
            <use href="#canvas"  />
            <use href="#shape2"  />
        </mask>
    </defs>
    <use href="#shape1" stroke="red" fill="none" mask="url(#shape2-cutout)" />
    <use href="#shape2" stroke="red" fill="none" mask="url(#shape1-cutout)" />
</svg>

这实质上是用切出的矩形绘制圆,然后用切出的圆画出矩形。当您将这些“打孔”形状一个一个叠放在另一个上时,您会得到一个看起来像单个轮廓的形状。

这是 SVG 的实际作用:

  1. 它定义了一个名为“canvas”的白色矩形,其大小与 SVG 相同。
  2. 它定义了要组合的两个形状(“shape1”和“shape2”)。
  3. 它为每个形状定义了一个遮罩,将画布(填充为白色)与形状(默认填充为黑色)结合在一起。请注意,当您对形状应用蒙版时,会显示与蒙版的白色区域对应的形状部分,而隐藏与黑色部分对应的部分。
  4. 它使用另一个形状的蒙版绘制每个形状。

【讨论】:

  • 这种解决方案有一个很大的缺点:边界上的抗锯齿会在两个形状相交处创建半透明像素。这可以通过连接两个形状(而不是从画布中减去)来创建蒙版来避免。然后是一个黑色矩形或任何可用于阴影并受 one 蒙版限制的东西。
  • @Pascal,不确定我是否理解您的建议,但请随时使用您的解决方案为这个问题写一个新的答案。我不会生气的!
【解决方案2】:

您可以从这两个形状中创建一个&lt;mask&gt; 或&lt;clipPath&gt;,然后用它来掩盖第三个形状。然后,您可以对其应用投影。

<svg width="400" height="400">
  <defs>
    <clipPath id="shape">
      <rect x="40" y="50" width="40" height="70" />
      <circle cx="50" cy="50" r="50" />
    </clipPath>
    
    <filter id="shadow">
      <feGaussianBlur in="SourceAlpha" stdDeviation="3"/>
      <feOffset dx="3" dy="3"/>
      <feMerge>
        <feMergeNode/>
        <feMergeNode in="SourceGraphic"/>
      </feMerge>
    </filter>
  </defs>

  <g filter="url(#shadow)">
    <rect width="100%" height="100%" fill="red"
          clip-path="url(#shape)"/>
  </g>

</svg>

注意:如果你想知道为什么我们在这里将阴影应用到父 &lt;g&gt;,这是因为如果我们直接将它应用到 &lt;rect&gt;,那么阴影也会受到剪辑的影响。

【讨论】:

  • 这就是我要找的。谢谢!
【解决方案3】:

只在形状周围的一组上添加阴影有什么问题?

    <svg width="400" height="400">
  <defs>   
    <filter id="shadow">
      <feGaussianBlur in="SourceAlpha" stdDeviation="3"/>
      <feOffset dx="3" dy="3"/>
      <feMerge>
        <feMergeNode/>
        <feMergeNode in="SourceGraphic"/>
      </feMerge>
    </filter>
  </defs>

  <g filter="url(#shadow)">
      <rect x="40" y="50" width="40" height="70" fill="red"/>
      <circle cx="50" cy="50" r="50" fill="red"/>
  </g>

</svg>

【讨论】:

    【解决方案4】:

    注意:请注意,此解决方案仅提供轮廓的外部部分。

    @devuxer 的回答略有不同。可以通过蒙版创建阴影,然后将其应用于黑色形状。在https://stackoverflow.com/a/50884092/275195 中,使用集合论(A-B)+(B-A) 创建了两个掩码。然而,在渲染引擎中,像素光栅用于遮罩并应用抗锯齿。这会导致 A 和 B 边界处的掩码中的半透明像素。这可以通过像这样将形状应用于一个蒙版来避免(基本上是A+B 减去A+B 的轮廓):

    <svg>
      <!-- Masks seem to be pre-filled with black -->
      <mask id="union">
        <!-- Whatever is white in the mask will be visible of the shape the mask is applied to. -->
        <rect id="A" x="20" y="20" width="50" height="100" stroke="white" fill="none"></rect>
        <!-- Putting a second (, third, ...) shape into the mask will draw the shape over any existing shapes. -->
        <circle id="B" cx="80" cy="60" r="30" stroke="white" fill="none"></circle>
        <!-- Now we delete the inner part of the shapes by drawing black over them. -->
        <rect id="Ainner" x="20" y="20" width="50" height="100" fill="black"></rect>
        <circle id="Binner" cx="80" cy="60" r="30" fill="black"></circle>
      </mask>
      <!-- Applied to a full-screen red rectangle it cuts out the strokes from the mask. -->
      <rect id="A+B" x="0" y="0" width="100%" height="100%" fill="red" mask="url(#union)"></rect>
    </svg>

    【讨论】:

      【解决方案5】:

      对于所有想要填充和边框的人,这里是devuxers 和pascals 帖子的组合

      <svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <rect id="canvas" width="100%" height="100%" fill="white" />
          <rect id="rect" x="50" y="60" width="40" height="70" />
          <circle id="circle" cx="50" cy="50" r="40" />
      
          <clipPath id="shape">
            <use href="#rect" />
            <use href="#circle" />
          </clipPath>
          
          <mask id="maskRect">
            <use href="#canvas" />
            <use href="#rect" />
          </mask>
          
          <mask id="maskCircle">
            <use href="#canvas" />
            <use href="#circle" />
          </mask>    
        
        </defs>
        <rect width="100%" height="100%" fill="red" clip-path="url(#shape)" />
        <use href="#rect" stroke="green" stroke-width="2" fill="none" mask="url(#maskCircle)" />
        <use href="#circle" stroke="green" stroke-width="2" fill="none" mask="url(#maskRect)" />
      </svg>

      【讨论】:

        【解决方案6】:

        使用feMorphology 过滤器,您可以扩大和缩小对象 (MDN)。将它们结合起来,您可以创建一个简单的可重复使用的轮廓:制作一个放大的原件副本,绘制它,然后将其放在缩小的原件下方。

        <svg xmlns="http://www.w3.org/2000/svg" width="400" height="400">
            <defs>
                <filter id="outline">
                    <!-- Make a copy of the original,
                        grow it by half the stroke width,
                        call it outer -->
                    <feMorphology
                        operator="dilate"
                        radius="1"
                        result="outer"
                    />
                    <!-- Make another copy of the original,
                        shrink it by half the stroke width,
                        call it inner -->
                    <feMorphology
                        in="SourceGraphic"
                        operator="erode"
                        radius="1"
                        result="inner"
                    />
                    <!-- Create a color layer... -->
                    <feFlood
                        flood-color="black"
                        flood-opacity="1"
                        result="outlineColor"
                    />
                    <!-- ...and crop it by outer -->
                    <feComposite
                        operator="in"
                        in="outlineColor"
                        in2="outer"
                        result="coloredOuter"
                    />
                    <!-- Place the shrunk original over it -->
                    <feComposite
                        operator="over"
                        in="inner"
                        in2="coloredOuter"
                    />
                </filter>
            </defs>
        
            <g filter="url(#outline)">
                <rect x="50" y="60" width="40" height="70" fill="red" />
                <circle cx="60" cy="60" r="50" fill="red" />
            </g>
        </svg>

        如果您想让它透明,请将最后一个feComposite 中的operator 替换为xor:

        <!-- Knock off the shrunk one -->
        <feComposite
            operator="xor"
            in="inner"
            in2="coloredOuter"
        />
        

        唯一需要注意的是:更粗的笔划 - 即更大的扩张和侵蚀半径 - 曲线上可能看起来扭曲,1px + 1px 仍然可以。

        【讨论】:

          猜你喜欢
          • 2022-01-25
          • 1970-01-01
          • 1970-01-01
          • 2023-01-10
          • 1970-01-01
          • 2014-03-20
          • 2014-03-30
          • 1970-01-01
          相关资源
          最近更新 更多