How can I merge two shapes in svg?

前端 未结 4 922
情话喂你
情话喂你 2020-12-29 08:18

I have two shapes: circle and rectangle. Want to convert them into one figure. Are there any ways to do that in SVG code?

相关标签:
4条回答
  • 2020-12-29 08:42

    For anyone looking for the answer to the actual question of how to combine two outlined shapes into a single outlined shape (rather than putting a drop shadow on the combined shape), here is a possible solution:

    <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>

    This essentially draws the circle with the rectangle shape cut out of it and draws the rectangle with the circle cut out of it. When you place these "punched out" shapes one on top of the other, you get what appears to be a single outlined shape.

    Here's what the SVG actually does:

    1. It defines a white rectangle called "canvas" that is the same size as the SVG.
    2. It defines the two shapes that are to be combined ("shape1" and "shape2").
    3. It defines a mask for each shape that combines the canvas (which has a fill of white) with the shape (which has a fill of black by default). Note that when you apply a mask to a shape, the part of the shape that corresponds to the white area of the mask is shown, while the part that corresponds with black part is hidden.
    4. It draws each shape with the the mask of the other shape applied.
    0 讨论(0)
  • 2020-12-29 08:46

    You can make a <mask> or a <clipPath> from the two shapes and then use that to mask a third shape. You can then apply your drop shadow to that.

    <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>

    Note: if you are wondering why we are applying the drop shadow to a parent <g> here, it is because if we applied it directly to the <rect>, the drop shadow would be subject to the clip also.

    0 讨论(0)
  • 2020-12-29 08:50

    What's wrong with just a dropshadow on a group around the shapes?

        <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>

    0 讨论(0)
  • 2020-12-29 09:08

    With the feMorphology filter you can grow and shrink objects (MDN). Combining them, you can create a simply reusable outline: make an increased copy of the original, paint it, and place it under the shrunk original.

    <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>

    If you want to have it transparent, replace the operator in the last feComposite with xor:

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

    The only caveat: fatter strokes - ie larger dilate and erode radii - on curves may look distorted, with 1px + 1px it is still fine.

    0 讨论(0)
提交回复
热议问题