Mammoth-Winner-1579
Mammoth-Winner-1579

Reputation: 17

How can I get one SVG path/fill to only be drawn "inside" another, complex SVG path?

I have a bottle shape in SVG that has a decorative swirl and a few other lines crossing the interior such that I'm not sure what counts as "inside" and "outside" the path. I want to put a wave shape "inside" the bottle, but I'm not getting any positive results experimenting with <clipPath> and am not sure if the interior lines in the bottle are working against me.

What I currently have, where the wave shape lies on top of the bottle:

  * {
    margin: 0;
    box-sizing: border-box;
  }
  .container {
    height: 100dvh;
  }
  svg {
    display: block;
    padding-block: 5%;
  }
<div class="container">
<svg width="100%" height="100%" viewBox="0 0 250 457" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path id="bottle" d="M89.6174 72.5661C78.3761 70.8794 34.9222 93.2227 19 135.503M89.6174 72.5661V2H125M89.6174 72.5661H125M19 135.503C-0.902714 188.355 -0.0185633 201.762 9.29124 288.909C17.1881 362.829 33.8318 424.189 44.8889 432.248C60.9111 443.925 84 454.607 125 454.607M19 135.503C130.2 168.307 155 202.255 125 251.842 M160.383 72.5661C171.624 70.8794 215.078 93.2227 231 135.503M160.383 72.5661V2H125M160.383 72.5661H125M231 135.503C250.903 188.355 250.019 201.762 240.709 288.909C232.812 362.829 216.168 424.189 205.111 432.248C189.089 443.925 166 454.607 125 454.607M231 135.503C119.8 168.307 95 202.255 125 251.842" stroke="#B9B9B9" stroke-width="4"/>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="130px"
     viewBox="0 0 136 195" style="enable-background:new 0 0 136 195;" xml:space="preserve">
         <path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
    </svg>
</svg>
</div>

Surrounding the bottle path with a <clipPath> tag just results in the wave shape alone appearing:

  * {
    margin: 0;
    box-sizing: border-box;
  }
  .container {
    height: 100dvh;
  }
  svg {
    display: block;
    padding-block: 5%;
  }
<div class="container">
<svg width="100%" height="100%" viewBox="0 0 250 457" fill="none" xmlns="http://www.w3.org/2000/svg">
    <clipPath>
    <path id="bottle" d="M89.6174 72.5661C78.3761 70.8794 34.9222 93.2227 19 135.503M89.6174 72.5661V2H125M89.6174 72.5661H125M19 135.503C-0.902714 188.355 -0.0185633 201.762 9.29124 288.909C17.1881 362.829 33.8318 424.189 44.8889 432.248C60.9111 443.925 84 454.607 125 454.607M19 135.503C130.2 168.307 155 202.255 125 251.842 M160.383 72.5661C171.624 70.8794 215.078 93.2227 231 135.503M160.383 72.5661V2H125M160.383 72.5661H125M231 135.503C250.903 188.355 250.019 201.762 240.709 288.909C232.812 362.829 216.168 424.189 205.111 432.248C189.089 443.925 166 454.607 125 454.607M231 135.503C119.8 168.307 95 202.255 125 251.842" stroke="#B9B9B9" stroke-width="4"/>
    </clipPath>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="130px"
     viewBox="0 0 136 195" style="enable-background:new 0 0 136 195;" xml:space="preserve">
         <path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
    </svg>
</svg>
</div>

Surrounding the wave path with <clipPath> just displays the bottle:

  * {
    margin: 0;
    box-sizing: border-box;
  }
  .container {
    height: 100dvh;
  }
  svg {
    display: block;
    padding-block: 5%;
  }
<div class="container">
<svg width="100%" height="100%" viewBox="0 0 250 457" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path id="bottle" d="M89.6174 72.5661C78.3761 70.8794 34.9222 93.2227 19 135.503M89.6174 72.5661V2H125M89.6174 72.5661H125M19 135.503C-0.902714 188.355 -0.0185633 201.762 9.29124 288.909C17.1881 362.829 33.8318 424.189 44.8889 432.248C60.9111 443.925 84 454.607 125 454.607M19 135.503C130.2 168.307 155 202.255 125 251.842 M160.383 72.5661C171.624 70.8794 215.078 93.2227 231 135.503M160.383 72.5661V2H125M160.383 72.5661H125M231 135.503C250.903 188.355 250.019 201.762 240.709 288.909C232.812 362.829 216.168 424.189 205.111 432.248C189.089 443.925 166 454.607 125 454.607M231 135.503C119.8 168.307 95 202.255 125 251.842" stroke="#B9B9B9" stroke-width="4"/>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="130px"
     viewBox="0 0 136 195" style="enable-background:new 0 0 136 195;" xml:space="preserve">
         <clipPath>
         <path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
         </clipPath>
    </svg>
</svg>
</div>

This is the bottle path loaded in a browser SVG editor: https://yqnn.github.io/svg-path-editor/#P=M89.6174_72.5661C78.3761_70.8794_34.9222_93.2227_19_135.503M89.6174_72.5661V2H125M89.6174_72.5661H125M19_135.503C-0.902714_188.355_-0.0185633_201.762_9.29124_288.909C17.1881_362.829_33.8318_424.189_44.8889_432.248C60.9111_443.925_84_454.607_125_454.607M19_135.503C130.2_168.307_155_202.255_125_251.842M160.383_72.5661C171.624_70.8794_215.078_93.2227_231_135.503M160.383_72.5661V2H125M160.383_72.5661H125M231_135.503C250.903_188.355_250.019_201.762_240.709_288.909C232.812_362.829_216.168_424.189_205.111_432.248C189.089_443.925_166_454.607_125_454.607M231_135.503C119.8_168.307_95_202.255_125_251.842

Upvotes: 0

Views: 62

Answers (2)

ccprog
ccprog

Reputation: 21921

A lot of things need to be done to make your code work. But first, a bit of cleanup:

  1. version="1.1", style="enable-background:new 0 0 136 195;" and xml:space="preserve" have absolutely no effect. Just delete them.
  2. xmlns="http://www.w3.org/2000/svg" and xmlns:xlink="http://www.w3.org/1999/xlink" are not needed if you write SVG code inline inside a HTML page. But note that if you leave out the xmlns:xlink attribute, all occurences of xlink:href have also to be shortened to href.
  3. You want to draw the lines representing the bottle in front of the blue area representing the content. Therefor, it needs to be further down in the source code.

Lets look at an interim result, leaving off the clip path:

* {
    margin: 0;
    box-sizing: border-box;
  }
  .container {
    height: 100dvh;
  }
  svg {
    display: block;
    padding-block: 5%;
  }
<div class="container">
<svg width="100%" height="100%" viewBox="0 0 250 457" fill="none">
    <path id="bottle" d="M89.6174 72.5661C78.3761 70.8794 34.9222 93.2227 19 135.503M89.6174 72.5661V2H125M89.6174 72.5661H125M19 135.503C-0.902714 188.355 -0.0185633 201.762 9.29124 288.909C17.1881 362.829 33.8318 424.189 44.8889 432.248C60.9111 443.925 84 454.607 125 454.607M19 135.503C130.2 168.307 155 202.255 125 251.842 M160.383 72.5661C171.624 70.8794 215.078 93.2227 231 135.503M160.383 72.5661V2H125M160.383 72.5661H125M231 135.503C250.903 188.355 250.019 201.762 240.709 288.909C232.812 362.829 216.168 424.189 205.111 432.248C189.089 443.925 166 454.607 125 454.607M231 135.503C119.8 168.307 95 202.255 125 251.842" stroke="#B9B9B9" stroke-width="4"/>
    <svg x="0px" y="130px" viewBox="0 0 136 195">
         <path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
    </svg>
</svg>
</div>

Now the most important part: a clip-path expresses a relationship between two entities: a grafical element, in your case a <svg> element, is clipped to an area expressed as a geometrical form, here just another <path> element. Wrapping one element with a <clipPath> does exactly nothing, as the relation to the other element is missing.

The general pattern looks like this

<clipPath id="clip">
    <path id="form" d="..."/>
</clipPath>

<path id="grafic d="..." clip-path="url(#clip)"/>

The relationship "the grafic is clipped by the form" is expressed with the attribute clip-path="url(#clip)". Everything written inside the <clipPath> element is not rendered. It is only interpreted as the source of the clipping.

In your case, things get complicated by the application of a clip path to a <svg> element. The reasoning how attributes viewBox and clip-path interact and why results look like they do is very subtle and not easy to understand. Most of the time, it is just easier to wrap the <svg> with a <g> element, and apply the clip path to that.

If you want to have a path used both as the form to use as a clipping source, and drawn as a grafic, it needs to appear in two places in your code. Fortunately, you can easily reuse content with a <use> element. The general pattern looks like this:

<clipPath id="clip">
    <path id="form" d="..."/>
</clipPath>

<path id="grafic d="..." clip-path="url(#clip)"/>
<use href="#form"/>

But before you do that, think about your grafic, and how it was represented in the browser editor. The areas shown on grey are what will be interpreted as "inside", everything else is "outside". This is clearly not what you meant. In your case, the reason lies in the somewhat chaotic structure of the path, which is actually composed of nine (!) independent subpaths, each of which have their own insides/outsides.

Cleaning up that code is a task that the website SvgPathEditor is not really suited for. You need a full-featured vector grafic editor like Illustrator or Inkscape to effectivly do that. What exactly needs to be done is too complex for a short description here, I just present you with the result. You need to have one form that represents the outer form, and a separate one for everything shown in the interior of that form. Only the first is used for the clipping:

<path d="M205.111 432.248c11.057-8.059 27.701-69.419 35.598-143.339 9.31-87.147 10.194-100.554
-9.709-153.406-15.922-42.2803-59.376-64.6236-70.617-62.9369V2H89.6174V72.5661C78.3761
70.8794 34.9222 93.2227 19 135.503-.9027 188.355-.0186 201.762 9.2912 288.909c7.8969
73.92 24.5406 135.28 35.5977 143.339C60.9111 443.925 84 454.607 125 454.607s64.089-10.682
80.111-22.359Z"/>

<path d="M19 135.503c111.2 34.307 136 66.752 106 116.339-30
-49.587-5.2-83.535 106-116.339M89.6174 72.5661H160.383"/>

* {
    margin: 0;
    box-sizing: border-box;
  }
  .container {
    height: 100dvh;
  }
  svg {
    display: block;
    padding-block: 5%;
  }
<div class="container">
<svg width="100%" height="100%" viewBox="0 0 250 457">
    <clipPath id="clip">
        <path id="bottle" d="M205.111 432.248c11.057-8.059 27.701-69.419 35.598-143.339 9.31-87.147 10.194-100.554-9.709-153.406-15.922-42.2803-59.376-64.6236-70.617-62.9369V2H89.6174V72.5661C78.3761 70.8794 34.9222 93.2227 19 135.503-.9027 188.355-.0186 201.762 9.2912 288.909c7.8969 73.92 24.5406 135.28 35.5977 143.339C60.9111 443.925 84 454.607 125 454.607s64.089-10.682 80.111-22.359Z" fill="none" stroke="#B9B9B9" stroke-width="4"/>
    </clipPath>
    <g clip-path="url(#clip)">
        <svg x="0px" y="130px" viewBox="0 0 136 195">
           <path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
        </svg>
    </g>
    <use href="#bottle"/>
    <path id="bottle" d="M19 135.503c111.2 34.307 136 66.752 106 116.339-30-49.587-5.2-83.535 106-116.339M89.6174 72.5661H160.383" fill="none" stroke="#B9B9B9" stroke-width="4"/>
</svg>
</div>

Upvotes: 3

Brett Donald
Brett Donald

Reputation: 14467

  1. Start with two bottles, an empty one (stroke but no fill) and a full one (fill but no stroke)
  2. Apply a wavy clip-path to the full bottle
  3. Stack the empty bottle on top of the full one

enter image description here

html, body {
  height: 100%;
}

body {
  margin: 0;
  padding: 1em;
  display: flex;
  justify-content: center;
  box-sizing: border-box;
}
<svg viewBox="0 0 250 457" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <clipPath id="clip-0">
      <path d="M 2.351 173.377 C 2.351 173.377 23.671 187.243 46.351 187.523 C 70.171 187.817 76.561 174.532 100.371 175.097 C 123.381 175.643 133.281 189.055 156.301 189.408 C 167.591 189.581 192.131 175.828 209.071 174.351 C 228.711 172.637 250.341 184.136 250.121 184.142 L 250.121 457.042 L 0.121 457.042 L 0.121 173.342 L 2.351 173.377 Z" style="stroke: rgb(0, 0, 0); stroke-width: 0px; fill: rgb(255, 255, 255);"/>
    </clipPath>
  </defs>
  <path id="path-1" d="M 125.081 454.65 C 84.081 454.65 60.991 443.968 44.971 432.291 C 33.911 424.232 17.271 362.872 9.371 288.952 C 0.061 201.802 2.651 173.422 19.081 135.542 C 35.001 93.262 77.681 74.122 89.681 72.642 L 89.781 2.042 L 160.461 2.042 L 160.481 72.642 C 173.281 74.402 215.161 93.262 231.081 135.542 C 246.741 169.742 250.101 201.802 240.791 288.952 C 232.891 362.872 216.251 424.232 205.191 432.291 C 189.171 443.968 166.081 454.65 125.081 454.65 Z" stroke="#B9B9B9" style="clip-path: url(&quot;#clip-0&quot;); fill: rgb(0, 251, 255); stroke-width: 0px;"/>
  <path id="bottle" d="M 125.121 454.649 C 84.121 454.649 61.031 443.967 45.011 432.29 C 33.951 424.231 17.311 362.871 9.411 288.951 C 0.101 201.804 2.691 173.423 19.121 135.542 C 35.041 93.265 77.721 74.119 89.721 72.642 L 89.821 2.042 L 160.501 2.042 L 160.521 72.642 C 173.321 74.404 215.201 93.265 231.121 135.542 C 246.781 169.746 250.141 201.804 240.831 288.951 C 232.931 362.871 216.291 424.231 205.231 432.29 C 189.211 443.967 166.121 454.649 125.121 454.649 Z" stroke="#B9B9B9" stroke-width="4" style="fill: none;"/>
</svg>

Using an SVG editor is the best way to achieve these results. I recommend Boxy SVG.

Upvotes: 4

Related Questions