目前正在构建一个基于浏览器的 SVG 应用程序。在此应用程序中,用户可以设置各种形状的样式和位置,包括矩形。
当我将 lines-width
应用于 1px
的 SVG rect
元素时,笔划将应用于 rect
> 不同浏览器的偏移和插入方式不同。事实证明,这很麻烦,尤其是当我尝试计算矩形的外部宽度和视觉位置并将其放置在其他元素旁边时。
例如:
- Firefox 添加 1px 插入(底部和左侧)和 1px 偏移(顶部和右侧)
- Chrome 添加 1px 插入(顶部和左侧)和 1px 偏移(底部和右侧)
到目前为止我唯一的解决方案是绘制实际的我自己设置边框(可能使用 path
工具)并将边框放置在描边元素后面。但这个解决方案是一个令人不愉快的解决方法,如果可能的话,我不想走这条路。
所以我的问题是,你能控制 SVG 的描边宽度如何在元素上绘制吗?
Currently building a browser-based SVG application. Within this app, various shapes can be styled and positioned by the user, including rectangles.
When I apply a stroke-width
to an SVG rect
element of say 1px
, the stroke is applied to the rect
’s offset and inset in different ways by different browsers. This is proving to be troublesome, especially when I try to calculate the outer width and visual position of a rectangle and position it next to other elements.
For example:
- Firefox adds 1px inset (bottom and left), and 1px offset (top and right)
- Chrome adds 1px inset (top and left), and 1px offset (bottom and right)
My only solution so far would be to draw the actual borders myself (probably with the path
tool) and position the borders behind the stroked element. But this solution is an unpleasant workaround, and I’d prefer not to go down this road if possible.
So my question is, can you control how an SVG’s stroke-width
is drawn on elements?
发布评论
评论(14)
不可以,您无法指定笔划是在元素内部还是外部绘制。为此,我向 SVG 工作组提出了提案该功能于 2003 年推出,但没有得到支持(或讨论)。
正如我在提案中指出的那样,
编辑:这个答案将来可能是错误的。使用 SVG 矢量效果应该可以实现这些结果,通过将
veStrokePath
与veIntersect
(用于“内部”)或与veExclude
(用于“外部”)相结合。然而,矢量效果仍然是一个工作草案模块,我还没有找到任何实现。编辑 2:SVG 2 草案规范包括
笔划-alignment
属性(具有 center|inside|outside 可能值)。该属性最终可能会进入 UA。编辑 3:有趣又令人失望的是,SVG 工作组已从 SVG 2 中删除了
笔划对齐
。您可以看到散文 此处。No, you cannot specify whether the stroke is drawn inside or outside an element. I made a proposal to the SVG working group for this functionality in 2003, but it received no support (or discussion).
As I noted in the proposal,
Edit: This answer may be wrong in the future. It should be possible to achieve these results using SVG Vector Effects, by combining
veStrokePath
withveIntersect
(for 'inside') or withveExclude
(for 'outside). However, Vector Effects are still a working draft module with no implementations that I can yet find.Edit 2: The SVG 2 draft specification includes a
stroke-alignment
property (with center|inside|outside possible values). This property may make it into UAs eventually.Edit 3: Amusingly and dissapointingly, the SVG working group has removed
stroke-alignment
from SVG 2. You can see some of the concerns described after the prose here.我找到了一种简单的方法,它有一些限制,但对我有用:
这里有一个工作示例:
I found an easy way, which has a few restrictions, but worked for me:
Here a working example:
更新:
lines-alignment
属性 于 4 月发布2015 年 1 月 1 日,采用了名为 SVG Strokes 的全新规范。截至 2015 年 2 月 26 日的 SVG 2.0 编辑草案(可能自 2 月 13 日起) , <删除>
stroke-alignment
属性存在 以及值内部
、中心
(默认)和外部
。它的工作方式似乎与 @Phrogz 和后来的
笔画位置
建议。该房产至少从 2011 年就开始规划,但除了注释说,它从未在规范中详细说明,因为它被推迟了 - 直到现在,似乎如此。
目前还没有浏览器支持此属性,或者据我所知,支持任何新的 SVG 2 功能,但希望随着规范的成熟,它们会很快支持。这是我个人一直敦促拥有的属性,我真的很高兴它终于出现在规范中。
关于该属性在开放路径和循环上的表现似乎存在一些问题。这些问题很可能会延长跨浏览器的实现时间。但是,当浏览器开始支持此属性时,我将用新信息更新此答案。
UPDATE: The
stroke-alignment
attribute was on April 1st, 2015 moved to a completely new spec called SVG Strokes.As of the SVG 2.0 Editor’s Draft of February 26th, 2015 (and possibly since February 13th),
thewith the valuesstroke-alignment
property is presentinner
,center
(default) andouter
.It seems to work the same way as the
stroke-location
property proposed by @Phrogz and the laterstroke-position
suggestion. This property has been planned since at least 2011, but apart from an annotation that said, it has never been detailed in the spec as it was deferred - until now, it seems.
No browser support this property, or, as far as I know, any of the new SVG 2 features, yet, but hopefully they will soon as the spec matures. This has been a property I personally have been urging to have, and I'm really happy that it's finally there in the spec.
There seems to be some issues as to how to the property should behave on open paths as well as loops. These issues will, most probably, prolong implementations across browsers. However, I will update this answer with new information as browsers begin to support this property.
您可以使用 CSS 来设置描边和填充顺序的样式。即先描边,再填充,即可得到想要的效果。
MDN 关于
paint-order
: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/paint-orderCSS 代码:
You can use CSS to style the order of stroke and fills. That is, stroke first and then fill second, and get the desired effect.
MDN on
paint-order
: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/paint-orderCSS code:
这是一个函数,它将计算您需要添加多少像素(使用给定的笔划)到顶部、右侧、底部和左侧,所有这些都基于浏览器:
Here's a function that will calculate how many pixels you need to add - using the given stroke - to the top, right, bottom and left, all based on the browser:
正如上面的人所指出的,您要么必须重新计算笔划路径坐标的偏移量,要么将其宽度加倍,然后遮盖一侧或另一侧,因为不仅 SVG 本身不支持 Illustrator 的笔划对齐,而且 PostScript 也不支持。
Adobe PostScript 手册第二版中的笔划规范指出:
“4.5.1 抚摸:
描边运算符沿着当前路径绘制一条一定粗细的线。对于路径中的每个直线或曲线段,描边会绘制一条以该段为中心且边与该段平行的线。" (强调他们的)
规范的其余部分没有用于偏移线位置的属性。当 Illustrator 让您在内部或外部对齐时,它会重新计算实际路径的偏移(因为它在计算上仍然比叠印路径更便宜)。 .ai 文档中的坐标是参考,而不是光栅化或导出为最终格式的内容,
因为 Inkscape 的本机格式是规范 SVG,因此它无法提供规范所缺乏的功能。
As people above have noted you'll either have to recalculate an offset to the stroke's path coordinates or double its width and then mask one side or the other, because not only does SVG not natively support Illustrator's stroke alignment, but PostScript doesn't either.
The specification for strokes in Adobe's PostScript Manual 2nd edition states:
"4.5.1 Stroking:
The stroke operator draws a line of some thickness along the current path. For each straight or curved segment in the path, stroke draws a line that is centered on the segment with sides parallel to the segment." (emphasis theirs)
The rest of the specification has no attributes for offsetting the line's position. When Illustrator lets you align inside or outside, it's recalculating the actual path's offset (because it's still computationally cheaper than overprinting then masking). The path coordinates in the .ai document are reference, not what gets rastered or exported to a final format.
Because Inkscape's native format is spec SVG, it can't offer a feature the spec lacks.
2023 年更新:当前草案将该属性重命名为 < code>lines-align
浏览器支持 2023:
请参阅 caniuse
类似 Polyfill 的辅助函数
基于之前组合
paint-order
、mask
和clip-path
的方法。(正如@Xavier Ho
@Jorg Janke)
这个助手/polyfill 实际上复制了 Adobe Illustrator 等应用程序的行为。
通过 paper.js offset glenzli 插件 进行硬编码偏移
这种方法实际上会增大/缩小你的
元素来获取所需的笔划位置(使用默认的中间笔划对齐)。
然而,图书馆在处理复杂的形状方面遇到了困难。
SVG 草稿文档中非常令人困惑的是关于几个“边缘情况”(例如开放或自相交路径)的问题注释。
事实上,SVG 功能应该适应开发人员/设计师已经从 Adobe Illustrator、Affinity Designer、Figma 等应用程序中了解的常见行为。
我还在 SVGWG github 存储库中发表了评论。
“‘笔画对齐’属性问题:为什么不调整图形应用程序的行为?”
Update 2023: The current draft renamed the attribute to
stroke-align
Browser Support 2023:
See caniuse
Polyfill-like helper function
Based on the previous approaches to combine
paint-order
,mask
andclip-path
.(As suggested by @Xavier Ho
@Jorg Janke)
This helper/polyfill actually replicates the behavior from applications like Adobe Illustrator.
Hardcoded offset via paper.js offset glenzli's plugin
This approach will actually grow/shrink your
<path>
elements to get the desired stroke position (using the default middle stroke-alignment).However, the library struggles with complex shapes.
What's quite confusing in the SVG drafts docs are the issue annotations about several "edge cases" like open or self-intersecting paths.
In fact the SVG feature should adapt the common behavior devs/designers already know from applications like Adobe Illustrator, Affinity Designer, Figma etc.
I also posted a comment in the SVGWG github repo.
"‘stroke-align’ property issues: why not adapt graphic apps behavior?"
这是使用
symbol
和use
解决内边框rect
的方法。示例:https://jsbin.com/yopemiwame/edit?html,输出
SVG:
注意:请务必将
use
中的?
替换为实际值。背景:之所以有效,是因为symbol通过用
svg
替换symbol
并在shadow DOM中创建一个元素来建立一个新的视口。然后,影子 DOM 的svg
会链接到您当前的SVG
元素。请注意,svg
可以嵌套,并且每个svg
都会创建一个新视口,该视口会剪辑重叠的所有内容,包括重叠边框。有关正在发生的事情的更详细概述,请阅读 Sara Soueidan 撰写的这篇精彩文章。Here is a work around for inner bordered
rect
usingsymbol
anduse
.Example: https://jsbin.com/yopemiwame/edit?html,output
SVG:
Note: Make sure to replace the
?
inuse
with real values.Background: The reason why this works is because symbol establishes a new viewport by replacing
symbol
withsvg
and creating an element in the shadow DOM. Thissvg
of the shadow DOM is then linked into your currentSVG
element. Note thatsvg
s can be nested and everysvg
creates a new viewport, which clips everything that overlaps, including the overlapping border. For a much more detailed overview of whats going on read this fantastic article by Sara Soueidan.我不知道这会有多大帮助,但就我而言,我只是创建了另一个仅带有边框的圆圈并将其放置在另一个形状的“内部”。
I don’t know how helpful will that be but in my case I just created another circle with border only and placed it “inside” the other shape.
一个(肮脏的)可能的解决方案是使用模式,
这里是一个带有内部描边三角形的示例:
https://jsfiddle .net/qr3p7php/5/
A (dirty) possible solution is by using patterns,
here is an example with an inside stroked triangle :
https://jsfiddle.net/qr3p7php/5/
Xavier Ho 提出的将笔画宽度加倍并更改绘画顺序的解决方案非常出色,尽管只有在以下情况下才有效:填充是纯色,没有透明度。
我开发了其他方法,更复杂但适用于任何填充。它也适用于椭圆或路径(后者有一些具有奇怪行为的极端情况,例如与自身交叉的开放路径,但不多)。
诀窍是在两层中显示形状。一个没有描边(仅填充),另一个仅具有双倍宽度描边(透明填充),并通过显示整个形状的蒙版,但隐藏没有描边的原始形状。
The solution from Xavier Ho of doubling the width of the stroke and changing the paint-order is brilliant, although only works if the fill is a solid color, with no transparency.
I have developed other approach, more complicated but works for any fill. It also works in ellipses or paths (with the later there are some corner cases with strange behaviour, for example open paths that crosses theirselves, but not much).
The trick is to display the shape in two layers. One without stroke (only fill), and another one only with stroke at double width (transparent fill) and passed through a mask that shows the whole shape, but hides the original shape without stroke.
我发现的最简单的方法是将剪辑路径添加到圆圈中
添加
clip-path="circle()"
然后
描边宽度=“5 ” “
会神奇地变成内在5 像素描边,绝对半径 100 像素。The easiest way I found is to add clip-path into circle
Add
clip-path="circle()"
<circle id="circle" clip-path="circle()" cx="100" cy="100" r="100" fill="none" stroke="currentColor" stroke-width="5" />
Then the
stroke-width="5"
will magically become inner 5px stroke with absolute 100px radius.这对我有用:
This worked for me:
自从我自己寻找解决方案以来,我已经阅读了本主题中的答案。就我而言,我无法内联编辑 SVG,因此我需要使用外部 CSS 绘制笔画。这样做将使描边不完全可见,因为它是在路径的外部绘制的。路径变得比视图框更大,因此它将被隐藏。
解决此问题的一个简单方法是将
overflow:visible;
放在 svg 本身上。您可以添加描边宽度
一半大小的填充,以使其达到原始大小。I've read the answers in this topic since I was looking for a solution myself. In my case I couldn't edit the SVG inline so I needed to draw the stroke with external CSS. Doing this will make the stroke not fully visible because it's drawn on the outside of the path. The path becomes bigger than the viewbox so it will be hidden.
A simple fix for this is to put
overflow: visible;
on the svg itself. You can add a padding with half the size as thestroke-width
to make it the original size.