您可以控制SVG的笔划宽度的绘制方式吗?


204

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

当我将a stroke-width应用于rectsay 的SVG 元素时1px,笔触rect将由不同的浏览器以不同的方式应用于的offset和inset。事实证明这很麻烦,尤其是当我尝试计算矩形的外部宽度和可视位置并将其放置在其他元素旁边时。

例如:

  • Firefox会添加1px的插图(底部和左侧)和1px的偏移量(顶部和右侧)
  • Chrome会添加1px的插图(顶部和左侧)和1px的偏移量(底部和右侧)

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

所以我的问题是,您可以控制stroke-width在元素上绘制SVG的方式吗?


还有,你可以用它来实现此过滤器的黑客-但它不是一个很好的解决方案
迈克尔Mullany

2
paint-order您可以在其中指定一个参数,该填充应该在笔划的顶部进行渲染,这样您将获得“外部对齐”,请参阅jsfiddle.net/hne0kyLg/1
Ivan

找到了一种使用css'outline-'属性执行此操作的方法:codepen.io/badcat/pen/YVzmYY。不确定跨浏览器对此有什么支持,但可能很有用。
bplmp

Answers:


375

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

SVG proposed stroke-location example, from phrogz.net/SVG/stroke-location.svg

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

  • 您可以通过将笔触宽度加倍,然后使用剪切路径将对象剪切到自身,从而获得与“内部”相同的视觉效果。
  • 您可以通过将笔触宽度加倍,然后将对象的无笔触副本覆盖在其自身上,来获得与“外部”相同的视觉效果。

编辑:此答案将来可能是错误的。通过结合使用SVG矢量效果,应该有可能获得这些结果veStrokePathveIntersect(用于“内部”)或与veExclude(用于“外部”)。但是,Vector Effects仍然是一个有效的模块草案,尚无我能找到的实现。

编辑2:SVG 2规范草案包括一个stroke-alignment属性(可能的值|可能的|内部|内部)。此属性可能最终使其成为UA。

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


2
认为可能是这样。为了解决这个问题,我为getBBox()编写了一个名为getStrokedBBox()的包装函数。该包装器根据浏览器如何将笔触应用于形状的插入和偏移来返回BBox。它不是完美的(需要不断检查最新的浏览器版本),但是目前它确实提供了形状的外部宽度。
史蒂夫

6
@Phrogz也许我们会在十年过去之前看到它。svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint注释
frenchone

1
终于到了!我(其中一个)确实一直在敦促此物业到达。
2015年

我创建了一个svg-contour脚本来跟踪任何SVGGeometryElement的轮廓,可以将其用作笔触对齐的变通方法,有兴趣的人可以在这里
maioman

2
我可以在任何页面上为提案投票吗?谢谢。似乎荒谬,不支持。
mahish

57

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

截至2015年2月26日的SVG 2.0编辑器草案(可能自2月13日起),stroke-alignment物业存在带有值innercenter (默认)outer

它的工作方式似乎与stroke-location@Phrogz提出的属性以及后来的stroke-position建议相同。此属性至少自2011年以来就已规划,但注释中说

SVG 2应包含一种指定笔触位置的方法

,它至今尚未在规范中进行详细说明,因为它似乎已经推迟了

尚无浏览器支持此属性,或者据我所知,尚不支持SVG 2的任何新功能,但希望它们会在规格成熟时尽快支持。这是我个人一直希望拥有的属性,我很高兴它终于出现在规范中。

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


1
stroke-alignment在SVG笔划(W3C工作草案)中指定。同时,SVG 2 W3C编辑器草案说,笔触定位属性应在SVG规范中,位于svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint,但该规范已达到W3C候选推荐标准状态,并且没有此类属性在规范中,除了指向stroke-position提案的链接之外,似乎情况并非如此。
帕特里克·黑

47

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

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


2
最好的答案,我发现
埃德Kolosovsky

1
聪明的解决方案。感谢
Vince Yuan

这应该作为答案。使用<defs>和<use>是当前可用的最优雅的解决方案。
Nyerguds

不错的解决方案。您将如何扭转它以做出外部中风?
朗讯

为什么是顶层<use>?为什么不直接放置路径和剪切路径呢?效果很好:<svg width="240" height="240" viewBox="0 0 1024 1024"> <path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z" clip-path="url(#clip)" stroke="#0081C6" stroke-width="160" fill="#00d2b8"/> <clipPath id="clip"> <use xlink:href="#ld"/> </clipPath> </svg>。(是的,这是完全合理和正确的SVG,将在任何地方正确渲染。不要担心递归。)
克里斯·摩根

30

您可以使用CSS设置笔触和填充的顺序样式。也就是说,先进行笔划,然后再进行填充,以获得所需的效果。

MDN paint-order https //developer.mozilla.org/en-US/docs/Web/SVG/Attribute/paint-order

CSS代码:

paint-order: stroke;

太棒了!感谢您的分享
-BrunoFenzl,

1
链接的文档似乎没有指示笔画是在内部,外部还是居中?您能否阐明如何控制笔画绘制的位置?谢谢。
Crashalot

2
笔划一如既往地居中-但是,如果您有实心填充,则调整绘画顺序以首先描画笔划的效果是笔画的内半部被覆盖,这与绘制笔触具有相同的效果。外面有一半的中风。
克里斯·摩根

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

        }

    };

6

如上所述,您要么必须重新计算笔触的路径坐标的偏移量,要么必须将其宽度加倍,然后遮盖一侧或另一侧,因为SVG不仅不原生支持Illustrator的笔划对齐方式,而且PostScript也不支持。

在Adobe的的PostScript手册第二版状态中风说明书:“ 4.5.1抚摸: 所述行程操作者绘制一条线沿着电流路径一定厚度的对于路径中的每个直的或弯曲段。冲程绘制是一条线为中心上边平行的线段线段的线段。” (强调他们的)

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

因为Inkscape的本机格式是SVG规范,所以它无法提供该规范缺少的功能。


4

这是解决内部边框 rect使用symboluse

示例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实际值。

背景:为什么这个工作的原因是因为符号建立通过更换新的视口symbolsvg和创建阴影DOM元素。然后svg,此影子DOM链接到您的当前SVG元素中。请注意,svgs可以嵌套,并且每个都svg可以创建一个新的视口,该视口将剪切所有重叠的内容,包括重叠的边框。有关正在阅读的内容的更详细的概述 Sara Soueidan的精彩文章


2

我不知道这有多大用处,但就我而言,我只是创建了一个仅带边框的圆,并将其“放置”在其他形状的内部。


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>

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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.