Can you control how an SVG's stroke-width is d

2019-01-03 08:30发布

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?

标签: svg offset rect
9条回答
我只想做你的唯一
2楼-- · 2019-01-03 08:47

A (dirty) possible solution is by using patterns,

here is an example with an inside stroked triangle :

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>
查看更多
Viruses.
3楼-- · 2019-01-03 08:48

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:

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

        }

    };
查看更多
狗以群分
4楼-- · 2019-01-03 08:50

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.

查看更多
戒情不戒烟
5楼-- · 2019-01-03 08:54

I found an easy way, which has a few restrictions, but worked for me:

  • define the shape in defs
  • define a clip path referencing the shape
  • use it and double the stroke with as the outside is clipped

Here a working example:

<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>
查看更多
手持菜刀,她持情操
6楼-- · 2019-01-03 08:55

UPDATE: The stroke-alignment attribute was on April 1st 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), the stroke-alignment property is present with the values inner, center (default) and outer.

It seems to work the same way as the stroke-location property proposed by @Phrogz and the later stroke-position suggestion. This property has been planned since at least 2011, but apart from an annotation that said

SVG 2 shall include a way to specify stroke position

, 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.

查看更多
We Are One
7楼-- · 2019-01-03 08:59

Here is a work around for inner bordered rect using symbol and use.

Example: 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>

Note: Make sure to replace the ? in use with real values.

Background: The reason why this works is because symbol establishes a new viewport by replacing symbol with svg and creating an element in the shadow DOM. This svg of the shadow DOM is then linked into your current SVG element. Note that svgs can be nested and every svg 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.

查看更多
登录 后发表回答