你能控制如何绘制SVG笔画宽度吗?

时间:2011-08-30 09:49:07

标签: svg offset rect

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

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

例如:

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

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

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

11 个答案:

答案 0 :(得分:331)

不,您无法指定是在元素内部还是外部绘制笔划。我在2003年向SVG工作组提供了a proposal以获得此功能,但它没有得到任何支持(或讨论)。

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

正如我在提案中所说,

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

修改:此答案将来可能有误。应该可以使用SVG Vector EffectsveStrokePathveIntersect(用于'内部')或用veExclude(用于'外部)相结合来实现这些结果。但是,Vector Effects仍然是一个工作草案模块,没有我能找到的实现。

编辑2 :SVG 2草案规范包含stroke-alignment属性(中心| inside | outside可能值)。这个属性最终可能会进入UAs。

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

答案 1 :(得分:55)

更新 stroke-alignment属性was on April 1st moved to a completely new spec called SVG Strokes

截至2015年2月26日的SVG 2.0编辑草案(可能是February 13th), the stroke-alignment property is present ,其值为inner,{{1 } (默认)center

它似乎与@Phrogz和后来stroke-position suggestion提出的outer属性的工作方式相同。这个属性至少从2011年开始计划,但除了注释

之外
  

SVG 2应包括指定行程位置的方法

,它从未在规范中详细说明,因为它是延期 - 直到现在,似乎。

没有浏览器支持这个属性,或者据我所知,任何新的SVG 2功能,但希望他们很快就会随着规范的成熟而支持。这是我个人一直敦促拥有的财产,我很高兴它终于出现在规范中。

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

答案 2 :(得分:32)

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

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

这是一个工作示例:

&#13;
&#13;
<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>
&#13;
&#13;
&#13;

答案 3 :(得分:8)

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

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

CSS代码:

paint-order: stroke;

答案 4 :(得分: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
            };

        }

    };

答案 5 :(得分:6)

正如上面的人已经注意到你要么必须重新计算笔划的路径坐标的偏移量或加倍其宽度然后屏蔽一边或另一边,因为SVG不仅本身不支持Illustrator的笔划对齐,而且PostScript也没有不管怎样。

Adob​​e的PostScript手册第2版中的笔画规范说明: “ 4.5.1抚摸: stroke 运算符沿当前路径绘制一条线的粗线。对于路径中的每个直线段或弯曲线段,描边会在线段上居中绘制一条平行到线段的线。“(强调他们的)

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

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

答案 6 :(得分:3)

以下是使用rectsymbol 内边界 use的解决方法。

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

<强> 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>

注意:请务必将?中的use替换为实际值。

背景:之所以可行,是因为符号通过将symbol替换为svg并在影子DOM中创建元素来建立新的视口。然后,将此svg影子DOM链接到您当前的SVG元素。请注意,svg可以嵌套,每个svg都会创建一个新视口,该视口会剪切重叠的所有内容,包括重叠的边框。有关Sara Soueidan阅读this fantastic article的详细信息,请参阅更详细的信息。

答案 7 :(得分: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>

答案 8 :(得分:0)

我不知道会有多大帮助,但在我的情况下,我只是创建了另一个仅有边框的圆圈,并将其置于另一个形状的“内部”。

答案 9 :(得分: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>

答案 10 :(得分:-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" 将神奇地变成具有 100 像素绝对半径的内部 5 像素笔划。