Consider the following snippet, which contains two versions of an SVG with an arrowhead defined by a <marker>
. Lines and paths are styled with vector-effect: non-scaling-stroke
. The second version is simply the first version with all coordinates divided by 10 (and the marker id
changed to something else).
JSFiddle: https://jsfiddle.net/nkevo5ja
On Windows 10:
vector-effect: non-scaling-stroke
is removed, the arrowheads become the same size (but this breaks the line-width).Whose implementation is incorrect?
<!-- Styles -->
<svg width="0" height="0">
<style>
line, path {
stroke: black;
vector-effect: non-scaling-stroke;
}
</style>
</svg>
<!-- Larger version -->
<svg width="120px" height="100%" viewBox="0 0 400 300">
<defs>
<marker id="larger"
viewBox="0 -20 70 40"
refX="70" refY="0"
markerWidth="70" markerHeight="40"
orient="auto"
>
<path d="M 70 0 L 0 -20 L 0 20 z"/>
</marker>
</defs>
<line x1="400" y1="300" x2="0" y2="0" marker-end="url(#larger)"/>
</svg>
<!-- Smaller version (larger version with all coordinates divided by 10) -->
<svg width="120px" height="100%" viewBox="0 0 40 30">
<defs>
<marker id="smaller"
viewBox="0 -2 7 4"
refX="7" refY="0"
markerWidth="7" markerHeight="4"
orient="auto"
>
<path d="M 7 0 L 0 -2 L 0 2 z"/>
</marker>
</defs>
<line x1="40" y1="30" x2="0" y2="0" marker-end="url(#smaller)"/>
</svg>
Chromium issue: https://crbug.com/1107791
Firefox has the correct behavior, there is even a note in the specs about this exact case:
When ‘markerUnits’ has the value
strokeWidth
, the size of the marker is relative to the stroke-width after it has had any transforms applied that affect the width of the stroke in the user coordinate system for the stroke. This means that, for example, the vector-effect attribute with a value ofnon-scaling-stroke
will result in the markers also being non scaling.
strokeWidth
being the default value for markerUnits
.
Edit: Turns out my reading of the specs was wrong and that what should happen is the markerWidth
and markerHeight
are multiplied by the final computed strokewidth
of the stroked line – 1px here – just like Chrome did.
You can refer to Robert Longson's answer, who did push a fix for Firefox so it aligns with Chrome and the specs.
Using this exact snippet, you can get the same results in both Chrome and Firefox by setting this markerUnits
attribute to userSpaceOnUse
.
<!-- Styles -->
<svg width="0" height="0">
<style>
line, path {
stroke: black;
vector-effect: non-scaling-stroke;
}
</style>
</svg>
<!-- Larger version -->
<svg width="120px" height="100%" viewBox="0 0 400 300">
<defs>
<marker id="larger"
viewBox="0 -20 70 40"
refX="70" refY="0"
markerUnits="userSpaceOnUse"
markerWidth="70" markerHeight="40"
orient="auto"
>
<path d="M 70 0 L 0 -20 L 0 20 z"/>
</marker>
</defs>
<line x1="400" y1="300" x2="0" y2="0" marker-end="url(#larger)"/>
</svg>
<!-- Smaller version (larger version with all coordinates divided by 10) -->
<svg width="120px" height="100%" viewBox="0 0 40 30">
<defs>
<marker id="smaller"
viewBox="0 -2 7 4"
refX="7" refY="0"
markerUnits="userSpaceOnUse"
markerWidth="7" markerHeight="4"
orient="auto"
>
<path d="M 7 0 L 0 -2 L 0 2 z"/>
</marker>
</defs>
<line x1="40" y1="30" x2="0" y2="0" marker-end="url(#smaller)"/>
</svg>