仅在内部或仅在外部描绘SVG路径

12
考虑两个HTML SVG路径,一个正方形(class inside),一个矩形(class outside),高度相同。当我应用stroke-width: 10px时,描边会向内外各应用5pxFiddle

enter image description here

我应该如何仅描绘内部或仅描绘外部?

.inside { 
  stroke: #333;
  stroke-mode: inside;     // property does not exist
  stroke-width: 5px;
}

.outside {
   stroke: #333;
   stroke-mode: outside;   // property does not exist
   stroke-width: 5px;
}

如果没有这样的属性,是否有一种解决方法可以实现类似于:

enter image description here


内部矩形的大小不可调整? - BlakkM9
它可以适用于简单的路径,比如矩形,但是对于包含多个路径的复杂SVG,例如从正方形中移除的圆形,它能否工作? - Saravanabalagi Ramachandran
你可能想要查看这里所解释的剪辑路径(clip-paths):https://dev59.com/EWw05IYBdhLWcg3wcRYj#32162431,它似乎是目前唯一可行的解决方案,因为SVG工作组未能实现多个提案中的描边控制属性... - exside
是的,没错。当我创建SVG时,我从不使用描边,只用填充。当我想在某物周围画一个边框时,我就像这样绘制路径。 - BlakkM9
3
通过使用paint-order属性,您可以营造只在外侧描边的假象。该属性允许您控制填充和描边(以及绘画标记)绘制的顺序。如果使用paint-order: stroke;,SVG会先绘制描边,然后再进行填充。因此,描边的内部一半将被填充遮盖。 - enxaneta
1个回答

13

@enxaneta 上面的评论完全正确。

通常情况下,如果一个填充的矩形宽度为 48px,并且带有 stroke-width12pxstroke,那么这个矩形将显示为:

  • 一个宽度为 12px 的边框
  • 一个表面宽度为 36px

为什么是 36px 而不是 48px 呢?

因为画在矩形左侧的 12px 宽度的线条遮挡了矩形的 6px,而画在矩形右侧的 12px 宽度的线条也遮挡了矩形的 6px

请看这个例子,其中 stroke 的透明度为 50% - 您可以看到一半的 stroke 重叠在 fill 上:

svg rect {
  x: 10px;
  y: 10px;
  width: 200px;
  height: 100px;
  stroke: rgba(0, 0, 0, 0.5);
  stroke-width: 12px;
  fill: rgb(255, 0, 0);
}
<svg>
  <rect />
</svg>


创建12像素外边框的解决方案:

要创建一个外边框,解决方案是首先绘制描边,然后在其上绘制填充。

我们可以使用以下方法实现:

paint-order: stroke;

现在矩形将显示为:

  • 一个明显的 6px 宽的边框(因为 fill 覆盖在上面,导致边框的宽度减半)
  • 宽度为 48px

svg rect {
  x: 10px;
  y: 10px;
  width: 200px;
  height: 100px;
  stroke: rgba(0, 0, 0, 0.5);
  stroke-width: 12px;
  fill: rgb(255, 0, 0);
  paint-order: stroke;
}
<svg>
  <rect />
</svg>

最后,为了确保矩形显示为:

  • 一个 12px 宽的边框
  • 宽度为 48px

stroke-width12px 改为 24px(即将预期显示宽度加倍):

svg rect {
  x: 10px;
  y: 10px;
  width: 200px;
  height: 100px;
  stroke: rgb(0, 0, 0);
  stroke-width: 24px;      /* double the intended display width */
  fill: rgb(255, 0, 0);
  paint-order: stroke;
}
<svg>
  <rect />
</svg>


创建12像素内边框的解决方案:

要创建一个内边框,我们需要三个步骤而不是两个。

前两个步骤很简单:

  • stroke-width加倍(与外边框一样)
  • 使用paint-order: fill(而不是paint-order: stroke

svg rect {
  x: 10px;
  y: 10px;
  width: 200px;
  height: 100px;
  stroke: rgba(0, 0, 0, 0.5);
  stroke-width: 24px;      /* double the intended display width */
  fill: rgb(255, 0, 0);
  paint-order: fill;
}
<svg>
  <rect />
</svg>

第三步是定义并应用一个<clipPath>,该路径与具有“内部边框”的形状完全相同。

例如,如果形状为:

<rect width="200" height="100" />

然后 <clipPath> 应该是:

<clipPath id="my-clip-path">
  <rect width="200" height="100" /> <!-- Same as the shape -->
</clipPath>

工作示例:

#my-rect {
  stroke: rgb(0, 0, 0);
  stroke-width: 24px;
  fill: rgb(255, 0, 0);
  paint-order: fill;
  clip-path: url(#my-clip-path);
}

#my-rect,
#my-clip-path-rect {
  x: 10px;
  y: 10px;
  width: 200px;
  height: 100px;
}
<svg>
  <defs>
    <clipPath id="my-clip-path">
      <rect id="my-clip-path-rect" />
    </clipPath>
  </defs>
  
  <rect id="my-rect" />
</svg>


所有矩形共同组成:

svg {
  display: block;
  float: left;
  width: 220px;
  margin-left: 12px;
}

svg:nth-of-type(1) rect {
  x: 10px;
  y: 10px;
  width: 200px;
  height: 100px;
  stroke: rgba(0, 0, 0, 0.5);
  stroke-width: 12px;
  fill: rgb(255, 0, 0);
}

svg:nth-of-type(2) rect {
  x: 10px;
  y: 10px;
  width: 200px;
  height: 100px;
  stroke: rgba(0, 0, 0, 0.5);
  stroke-width: 24px;
  fill: rgb(255, 0, 0);
}

svg:nth-of-type(3) rect {
  x: 10px;
  y: 10px;
  width: 200px;
  height: 100px;
  stroke: rgb(0, 0, 0);
  stroke-width: 24px;
  fill: rgb(255, 0, 0);
  paint-order: stroke;
}

#svg3clip {
  width: 200px;
  height: 100px;
}

svg:nth-of-type(4) rect {
  x: 10px;
  y: 10px;
  width: 200px;
  height: 100px;
  stroke: rgb(0, 0, 0);
  stroke-width: 24px;
  fill: rgb(255, 0, 0);
  paint-order: fill;
  clip-path: url(#svg3clip);
}
<svg>
  <rect />
</svg>

<svg>
  <rect />
</svg>

<svg>
  <defs>
    <clipPath id="svg3clip">
      <rect />
    </clipPath>
  </defs>
  
  <rect />
</svg>

<svg>
  <rect />
</svg>


更多阅读资料:


谢谢,这对于外边框很有效。有没有针对内边框的解决方法? - Saravanabalagi Ramachandran
1
我思考了一下,@SaravanabalagiRamachandran和我想出了一个解决方案,可以使用paint-order: fill<clipPath> SVG元素(结合clip-path CSS属性)来实现内部边框。有关完整详细信息,请参见上文。 - Rounin
非常感谢,我很欣赏。我想指出,这个内部描边的解决方法受到了一个限制,即我们需要复制形状并使用它来进行裁剪。希望有一种方法可以将对象裁剪到自身,从而消除了复制的必要性。 - Saravanabalagi Ramachandran
1
是的 - 你应该能够完全做到这一点,@SaravanabalagiRamachandran,通过利用SVG非常有用的<use>元素。请参阅:https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use - Rounin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接