140

I would like to be able to set the stroke-width on an SVG element to be "pixel-aware", that is always be 1px wide regardless of the current scaling transformations applied. I am aware that this may well be impossible, since the whole point of SVG is to be pixel independent.

Context follows:

I have an SVG element with its viewBox and preserveAspectRatio attributes set. It looks something like this

<svg version="1.1" baseProfile="full"
    viewBox="-100 -100 200 200" preserveAspectRatio="xMidYMid meet"
    xmlns="http://www.w3.org/2000/svg" >
</svg>

This means that when I scale that element, the actual shapes inside it scale accordingly (so far so good).

As you can see, I have set up the viewBox so that the origin is in the center. I would like to draw an x- and a y-axis within that element, which I do thus:

<line x1="-1000" x2="1000" y1="0" y2="0" />

Again, this works fine. Ideally, though, this axis would always be only 1px wide. I have no interest in the axes getting fatter when i scale the parent svg element.

So am I screwed?

wxs
  • 5,617
  • 5
  • 36
  • 51

2 Answers2

181

You can use the vector-effect property set to non-scaling-stroke, see the docs. Another way is to use transform(ref).

That will work in browsers that support those parts from SVG Tiny 1.2, for example Opera 10. The fallback includes writing a small script to do the same, basically inverting the CTM and applying it on the elements that shouldn't scale.

If you want sharper lines you can also disable antialiasing (shape-rendering=optimizeSpeed or shape-rendering=crispEdges) and/or play with the positioning.

Zach Saucier
  • 24,871
  • 12
  • 85
  • 147
Erik Dahlström
  • 59,452
  • 12
  • 120
  • 139
  • 1
    Unfortunately this is in a XUL app, and that doesn't seem to support vector-effect yet. Oh well. – wxs Aug 20 '09 at 14:22
  • 1
    This should appear in Firefox 15 all being well so you should be able to use it once you build your XUL app on gecko 15. – Robert Longson May 19 '12 at 14:04
  • 3
    IE11 still doesn't support `vector-effect` property. Is it possible to achieve the same effect as `vector-effect: non-scaling-stroke` in IE11? – merlin Aug 04 '14 at 03:48
  • 1
    @merlin yes, with js it's possible to emulate this in IE. – Erik Dahlström Aug 04 '14 at 08:45
  • @ErikDahlström You mean inverting the CTM and applying it on the elements that shouldn't scale? But stroke is not represent by element. How can i apply the inverting to it? Would be awesome if you can provide a code snippet to illustrate it. Thanks in advance. – merlin Aug 04 '14 at 09:24
  • 3
    @merlin clone the element (setting `fill` to `none` and vice versa for the `stroke`), compute & set the appropriate transforms (one for the fill part and one for the stroke part). It's going to be a little messy for sure, but there it is - you might also want to ask Microsoft to add support for it. In any case I think your question deserves to be a question of its own. – Erik Dahlström Aug 04 '14 at 11:20
  • @ErikDahlström would it be possible for you to show some code which achieves what you described in the last comment? – Don Box Oct 28 '16 at 18:27
  • Should this approach work with browser zoom, or only SVG scaling transformations? – ZachB Dec 06 '16 at 21:47
  • I could not get non-scaling-stroke working in current Edge or FF versions. I had the impression, that the effects got even worse. https://stackoverflow.com/questions/10357292/how-to-make-stroke-width-immune-to-the-current-transformation-matrix gives some explanation. – cskwg Mar 27 '21 at 05:16
6

Here is a more concise answer based on Erik's answer to help you get started quickly.

<div style="background: blue; width: 100%; height: 130px;">
            <svg xml:id="root" viewBox="0 0 100 100" width="100%" height="100%" preserveAspectRatio="none">
                <rect xml:id="r" vector-effect="non-scaling-stroke" x="0" y="0" width="100" height="100" fill="none" stroke="#88CE02"
                      stroke-linecap="square" stroke-width="10" stroke-miterlimit="30"/>
            </svg>
        </div>

Adding the vector-effect="non-scaling-stroke" to the SVG rect makes the border size (or stroke size) fixed.

supersan
  • 5,671
  • 3
  • 45
  • 64