241

目前正在构建一个基于浏览器的 SVG 应用程序。在这个应用程序中,用户可以设置各种形状的样式和位置,包括矩形。

当我将 astroke-width应用于 say 的 SVGrect元素时,不同的浏览器以不同的方式1px将笔划应用于的偏移和插入。rect事实证明这很麻烦,尤其是当我尝试计算矩形的外部宽度和视觉位置并将其放置在其他元素旁边时。

例如:

  • Firefox 添加 1px 插入(底部和左侧)和 1px 偏移(顶部和右侧)
  • Chrome 添加 1px 插入(顶部和左侧)和 1px 偏移(底部和右侧)

到目前为止,我唯一的解决方案是自己绘制实际边框(可能使用path工具)并将边框放置在描边元素的后面。但是这个解决方案是一个令人不快的解决方法,如果可能的话,我宁愿不要走这条路。

所以我的问题是,你能控制 SVGstroke-width在元素上的绘制方式吗?

4

11 回答 11

437

不,您不能指定笔画是在元素内部还是外部绘制。我在 2003 年向 SVG 工作组提出了这个功能的建议,但没有得到支持(或讨论)。

SVG 提出的笔画位置示例,来自 phrogz.net/SVG/stroke-location.svg

正如我在提案中指出的那样,

  • 您可以通过将笔画宽度加倍然后使用剪切路径将对象剪切到自身来实现与“内部”相同的视觉效果,并且
  • 您可以通过将笔划宽度加倍然后在其自身顶部覆盖对象的无笔划副本来实现与“外部”相同的视觉效果。

编辑:这个答案将来可能是错误的。应该可以使用SVG 矢量效果,通过结合veStrokePath( veIntersectfor 'inside') 或 with veExclude(for 'outside) 来实现这些结果。但是,矢量效果仍然是一个工作草案模块,我还没有找到任何实现。

编辑 2:SVG 2 草案规范包括一个stroke-alignment属性(带有 center|inside|outside 可能的值)。该属性最终可能会成为 UA。

编辑 3:有趣且令人失望的是,SVG 工作组已从SVG 2 中删除。您可以在此处stroke-alignment看到散文后描述的一些问题。

于 2011-09-01T16:26:18.577 回答
70

更新:stroke-alignment属性于 2015 年 4 月 1 日移至名为 SVG Strokes 的全新规范

从 2015 年 2 月 26 日(可能从2 月 13 日开始)的 SVG 2.0 编辑草案开始,stroke-alignment物业存在使用值inner, center (默认)outer.

stroke-location它似乎与@Phrogz 提出的属性和后来的stroke-position建议的工作方式相同。这个属性至少从 2011 年就已经计划好了,但是除了一个注释说

SVG 2 应包括一种指定笔画位置的方法

,它从未在规范中详细说明,因为它被推迟了——似乎直到现在。

目前还没有浏览器支持这个属性,或者,据我所知,任何新的 SVG 2 特性,但希望它们会在规范成熟后尽快实现。这是我个人一直敦促拥有的属性,我真的很高兴它终于出现在规范中。

关于属性如何在开放路径和循环上表现似乎存在一些问题。这些问题很可能会延长跨浏览器的实现。但是,随着浏览器开始支持此属性,我将使用新信息更新此答案。

于 2015-02-28T22:10:36.013 回答
66

我找到了一种简单的方法,它有一些限制,但对我有用:

  • 在 defs 中定义形状
  • 定义引用形状的剪辑路径
  • 使用它并将笔画加倍,因为外部被剪裁

这是一个工作示例:

<svg width="240" height="240" viewBox="0 0 1024 1024">
<defs>
	<path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z"/>
	<clipPath id="clip">
		<use xlink:href="#ld"/>
	</clipPath>
</defs>
<g>
	<use xlink:href="#ld" stroke="#0081C6" stroke-width="160" fill="#00D2B8" clip-path="url(#clip)"/>
</g>
</svg>

于 2015-08-23T01:48:58.340 回答
53

您可以使用 CSS 来设置笔触和填充的顺序。即先描边,后填充,得到想要的效果。

MDN 上paint-orderhttps ://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/paint-order

CSS 代码:

paint-order: stroke;
于 2018-08-09T11:36:01.340 回答
7

这是一个函数,它将计算您需要添加多少像素 - 使用给定的笔画 - 到顶部、右侧、底部和左侧,所有这些都基于浏览器:

var getStrokeOffsets = function(stroke){

        var strokeFloor =       Math.floor(stroke / 2),                                                                 // max offset
            strokeCeil =        Math.ceil(stroke / 2);                                                                  // min offset

        if($.browser.mozilla){                                                                                          // Mozilla offsets

            return {
                bottom:     strokeFloor,
                left:       strokeFloor,
                top:        strokeCeil,
                right:      strokeCeil
            };

        }else if($.browser.webkit){                                                                                     // WebKit offsets

            return {
                bottom:     strokeCeil,
                left:       strokeFloor,
                top:        strokeFloor,
                right:      strokeCeil
            };

        }else{                                                                                                          // default offsets

            return {
                bottom:     strokeCeil,
                left:       strokeCeil,
                top:        strokeCeil,
                right:      strokeCeil
            };

        }

    };
于 2011-09-02T11:37:51.463 回答
6

正如上面的人所指出的,您要么必须重新计算笔划路径坐标的偏移量,要么将其宽度加倍,然后遮盖一侧或另一侧,因为 SVG 不仅不支持 Illustrator 的笔划对齐,而且 PostScript 也不支持.

Adobe 的 PostScript 手册第 2 版中的笔划规范指出:“ 4.5.1 笔划: 笔划运算符沿当前路径绘制一条粗细的线。对于路径中的每个直线或曲线段,笔划绘制一条平行于线段的线段。" (强调他们的)

规范的其余部分没有用于偏移线位置的属性。当 Illustrator 让您在内部或外部对齐时,它会重新计算实际路径的偏移量(因为它在计算上仍然比叠印然后蒙版便宜)。.ai 文档中的路径坐标是参考,而不是被光栅化或导出为最终格式的坐标。

因为 Inkscape 的原生格式是规范 SVG,所以它不能提供规范所缺乏的功能。

于 2014-11-04T20:59:24.980 回答
4

这是使用and的内部边界 的解决方法。rectsymboluse

示例https ://jsbin.com/yopemiwame/edit?html,输出

SVG

<svg>
  <symbol id="inner-border-rect">
    <rect class="inner-border" width="100%" height="100%" style="fill:rgb(0,255,255);stroke-width:10;stroke:rgb(0,0,0)">
  </symbol>
  ...
  <use xlink:href="#inner-border-rect" x="?" y="?" width="?" height="?">
</svg>

注意:确保将?in替换为use实际值。

背景:之所以可行,是因为 symbol 通过在 shadow DOM 中替换和创建一个元素来建立一个新的视symbolsvg。然后将这个svg影子 DOM 链接到您的当前SVG元素。请注意,svgs 可以嵌套,并且每个都svg创建一个新的视口,该视口会剪切所有重叠的内容,包括重叠的边框。有关正在发生的事情的更详细的概述,请阅读Sara Soueidan 的这篇精彩文章

于 2015-07-30T14:08:25.993 回答
2

我不知道这会有多大帮助,但在我的例子中,我只是创建了另一个只有边框的圆圈,并将它放在另一个形状的“内部”。

于 2016-11-08T15:34:38.833 回答
0

一个(肮脏的)可能的解决方案是使用模式,

这是一个内部描边三角形的示例:

https://jsfiddle.net/qr3p7php/5/

<style>
#triangle1{
  fill: #0F0;
  fill-opacity: 0.3;
  stroke: #000;
  stroke-opacity: 0.5;
  stroke-width: 20;
}
#triangle2{
  stroke: #f00;
  stroke-opacity: 1;
  stroke-width: 1;
}    
</style>

<svg height="210" width="400" >
    <pattern id="fagl" patternUnits="objectBoundingBox" width="2" height="1" x="-50%">
        <path id="triangle1" d="M150 0 L75 200 L225 200 Z">
    </pattern>    
    <path id="triangle2" d="M150 0 L75 200 L225 200 Z" fill="url(#fagl)"/>
</svg>
于 2015-06-03T15:46:36.583 回答
0

Xavier Ho将笔画宽度加倍并更改绘制顺序的解决方案非常出色,尽管只有在填充为纯色且没有透明度时才有效。

我开发了其他方法,更复杂但适用于任何填充。它也适用于椭圆或路径(后者有一些具有奇怪行为的极端情况,例如交叉的开放路径,但不多)。

诀窍是在两层中显示形状。一个没有描边(仅填充),另一个只有双倍宽度的描边(透明填充)并通过显示整个形状的蒙版,但隐藏了没有描边的原始形状。

  <svg width="240" height="240" viewBox="0 0 1024 1024">
  <defs>
    <path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z"/>
    <mask id="mask">
      <use xlink:href="#ld" stroke="#FFFFFF" stroke-width="160" fill="#FFFFFF"/>
      <use xlink:href="#ld" fill="#000000"/>
    </mask>
  </defs>
  <g>
    <use xlink:href="#ld" fill="#00D2B8"/>
    <use xlink:href="#ld" stroke="#0081C6" stroke-width="160" fill="red" mask="url(#mask)"/>
  </g>
  </svg>
于 2020-04-24T08:54:29.403 回答
-1

我发现最简单的方法是将剪辑路径添加到圆圈中

添加clip-path="circle()"

<circle id="circle" clip-path="circle()" cx="100" cy="100" r="100" fill="none" stroke="currentColor" stroke-width="5" />

然后stroke-width="5"将神奇地变成绝对半径为 100px 的内部 5px 笔划。

于 2021-05-08T03:18:15.777 回答