A common graphic element drawn with SVG are arrowheads. You could create a new one for each line, but that duplicates code. You could reuse a graphic you defined in <defs>
or <symbol>
elements, but then you have to move and rotate your arrowhead with each new line. Better would be to make things easy on yourself and use a <marker>
element.
The last couple of weeks I talked about how to organize your SVG code and how you could define graphical objects for later reuse.
Today I want to talk about a specific type of element that will always be defined in one place and referenced in another. I want to talk about markers, which are typically used to create arrowheads and other polymarkers.
The <marker>
Element
A marker is a type of symbol that gets attached to one or more vertices of a path, line, polyline, or polygon. The most common use is to draw arrowheads or graphical shapes that mark points (polymarkers) on a the resulting line(s).
You create a marker using the <marker>
element and its associated attributes. A marker is always placed inside a <defs>
element and you refer to it elsewhere to use it. The easiest way to understand markers is with a simple example.
[code type=html]
<svg width="600px" height="100px">
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#f00" />
</marker>
</defs>
<line x1="50" y1="50" x2="250" y2="50" stroke="#000" stroke-width="5" marker-end="url(#arrow)" />
</svg>
[/code]
This SVG contains a marker and a line that references the marker. Let’s focus on the marker first. Notice that it’s contained inside <defs>
so it won’t display until referenced. Also notice it’s been given an id of arrow.
Inside the marker is a path that creates a small red triangle. If you remember your line path commands, this one begins at a point (0,0) then draws a line to the point (0,6) or 6px down, then a line to the point (9,3), before the z command closes the path and draws a line back to (0,0). The path is filled with #f00 or red.
Ignore the attributes on the marker element (besides the arrow id) for a moment and look at the line element. It’s a black (#000) horizontal line 5px wide, and it’s drawn from the point (50,50) to the point (250,50).
At the end of the line element is an attribute named marker-end which references the marker with an id of arrow. Here’s the resulting graphic.
Let’s leave the marker alone and change the line so point in the opposite direction and also at a slight angle from horizontal.
[code type=html]
<svg width="600px" height="100px">
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#f00" />
</marker>
</defs>
<line x1="295" y1="50" x2="95" y2="75" stroke="#000" stroke-width="5" marker-end="url(#arrow)" />
</svg>
[/code]
This second line starts at (295,50) and ends at (95,75). The x and y values of the line are the only changes I made. I didn’t have to alter anything about the marker itself for it to appear properly at the end of the line.
The ability to adjust with what they’re being attached to is what makes markers special. Without a single change the <marker>
element, the arrowhead adjusted itself to the line.
Marker Attributes
Now let’s take a look at the attributes on the marker element. Here’s the code for the first line and arrowhead (the one pointing right) again.
[code type=html]
<svg width="600px" height="100px">
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#f00" />
</marker>
</defs>
<line x1="50" y1="50" x2="250" y2="50" stroke="#000" stroke-width="5" marker-end="url(#arrow)" />
</svg>
[/code]
In addition to the id of arrow, there are six attributes on this marker, that you can use on all markers.
The markerWidth and markerHeight attributes define the width and height of the marker’s viewport. The marker will display inside its own viewport, which should at least be as large as whatever graphics you want to show (unless your goal is to hide part of the shape).
In the example I set both markerWidth and markerHeight to 10 (px). The triangle drawn by the path needs to fit inside a 9px by 6px area so I could have set markerWidth to 9 and markerHeight to 6 and been fine. Anything smaller and the triangle would get cropped.
The next two attributes, refX and refY refer to the point on the marker that will be aligned with the vertices of the graphic element to which it’s being attached. A transition transform is used behind the scenes to move the marker into alignment.
In the example I moved the triangle up by setting its vertical midpoint (3) as the refY value. That moves the point (0,3) to the point of the graphic element where the marker gets attached.
The next attribute, orient, is why I didn’t need to alter the marker when I reversed the line. It takes a value of auto or an angle you set and it indicates how the marker should be rotated when it’s attached.
A value of auto means the marker will rotate with whatever element is being drawn. A value of something like 45deg will always orient the marker at the same 45 degree angle regardless of how the element it’s attached to is rotated. Most of the time a value of auto will be what you want.
For comparison here’s what both examples look like when orient is set to 45deg. Notice that the arrowhead is rotated the same in both examples. In the second it even gets cropped by the SVG viewport, which I’ve shown with the a outline.
The last attribute markerUnits determines the scale at which the marker is drawn. It defines the coordinate system for the markerWidth and markerHeight values as well as the contents of the marker.
It takes two possible values, strokeWidth and userSpaceOnUse. The default is strokeWidth and it’s likely the option you’ll want most of the time because it will allow your marker to scale with the line it’s being attached to.
strokeWidth — The marker values represent values in a coordinate system which has a single unit equal in size to the units of the current stroke width. In other words a value of strokeWidth allows your marker to scale.
userSpaceOnUse —The marker values represent values in the current user coordinate system. If your marker is a circle with a radius of 10px it will always have that same 10px radius regardless of what element it’s attached to.
In the original example in this post I used strokeWidth. I created a triangle that’s 9px horizontally and 6px vertically and I added it to a line with a stroke-width of 5px. If you measure the triangle in the resulting SVG you’ll notice it measures 45px by 30px (5 × 9 and 5 × 6 respectively).
Here’s the same SVG with a value of userSpaceOnUse for markerUnits. It displays the triangle as 9px by 6px.
While you might want the marker to remain the same absolute size at times, I suspect the more common use case will be a value of strokeWidth to allow your marker to scale with whatever it’s being attached to.
Last week I mentioned you could add a viewBox to the symbol element. You can also add a viewBox to the marker element for additional scaling. For example here I set markerUnits back to strokeWidth and added a viewBox to the marker element.
[code type=html]
<svg width="600px" height="100px">
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth" viewBox="0 0 20 20">
<path d="M0,0 L0,6 L9,3 z" fill="#f00" />
</marker>
</defs>
<line x1="50" y1="50" x2="250" y2="50" stroke="#000" stroke-width="5" marker-end="url(#arrow)" />
</svg>
[/code]
The viewBox (0 0 20 20) is twice the size of the marker viewport (markerWidth=“10” and markerHeight=“10”) so it should scale the triangle to half its original size. You can see this in the resulting graphic.
Adding a viewBox to a <symbol>
element will work the same way and have the same effect on the graphics drawn in the symbol.
Marker Properties — Referencing Markers in Elements
In all the examples to this point I’ve referenced the marker in the same way, by setting its id (as part of the url) on a marker-end attribute. The marker-end attribute is a property of markers that signals where to attach the marker.
[code type=html]
marker-end=”url(#arrow)”
[/code]
It’s only one of four possible properties you can use to set the marker on the line, path, polyline, or polygon. You’ll reference the marker the same way for each.
- marker-start=”url(#marker-id)”
- marker-mid=”url(#marker-id)”
- marker-end=”url(#marker-id)”
- marker=”url(#marker-id)”
Note: You can also set the above in your CSS as marker-end: url(“#arrow”);
You’ve seen marker-end already and marker-start does exactly what you think. Here’s the original example with the marker attached using marker-start.
Notice the arrow points in the same direction it did before. It doesn’t change orientation just because it’s attached to the end of the line.
You might naturally think marker-mid places the marker in the center of the line, but it doesn’t. In fact it won’t do anything if we attach the marker to marker-mid of a line element. The marker won’t appear at all.
The marker-mid property sets a marker at points where a polyline, path, or polygon changes direction. Here I created a second marker, a polymarker in the shape of a circle. I added the triangle marker to the end of a polyline, line, and path. On each I also added the circle marker at markerStart and markerMid.
[code type=html]
<svg width="600px" height="400px" class="example">
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth" viewBox="0 0 20 20">
<path d="M0,0 L0,6 L9,3 z" fill="#f00" />
</marker>
</defs>
<marker id="circle" markerWidth="4" markerHeight="4" refX="2" refY="2">
<circle cx="2" cy="2" r="2" stroke="none" fill="#f00"/>
</marker>
<polyline points="50,100 250,100 250,200 350,200" fill="none" stroke="#000" stroke-width="10" marker-end="url(#arrow)" marker-start="url(#circle)" marker-mid="url(#circle)" />
<path d="M50,100 l0,200 l50,0" stroke="#000" fill="none" stroke-width="10" marker-end="url(#arrow)" marker-start="url(#circle)" marker-mid="url(#circle)" />
<line x1="50" y1="100" x2="220" y2="270" stroke="#000" stroke-width="10" marker-end="url(#arrow)" marker-start="url(#circle)" marker-mid="url(#circle)"/>
</svg>
[/code]
Here’s the resulting graphic. There are actually three circles at the point where line, polyline, and path all start. You see only the top circle on the pile.
You can see the line (pointing 45 degrees down and to the right) doesn’t get a mid point circle at all. The polyline gets a circle both times it changes direction and the path gets one mid point circle when it changes direction.
The marker property, (without -start, -mid, or -end) is shorthand for all three, though I haven’t found it to work anywhere I’ve tried.
Closing Thoughts
Markers are a nice way to attach graphic elements to lines, paths, polylines, and polygons and they’re typically used for arrowheads and polymarkers like the circle in the last example.
You can use a handful of attributes to control the size and location of your marker. You can scale it to match what it’s being attach to or keep it at an absolute size always. Four marker properties allow you to attach your marker at various points on lines, polylines, polygons, and paths.
Next week I want to talk about yet another element used to define graphic elements for later use. I want to talk about the pattern element and how you can use it to create pattern fills on different elements.
Download a free sample from my book, Design Fundamentals.
Nice example, but how do i Inherit color to arrow from parent stroke color ?
The line isn’t the parent of the arrow. There isn’t anything to inherit between them.
This is a fine example of a “programmer answer”. I.e. I understand what you are asking, but you did not ask the right question exactly rights, so I won’t help you with your actual problem, instead I will point out the problem with our question. 🙂
Not at all. The question doesn’t make sense to me. I mentioned why. I’m not saying I won’t help, but I don’t know how to help with a question I don’t understand.
Can you answer Timur’s question or were you only interested in criticizing me?
I’ll try to rephrase the question from Timur Junussov. How can I use arrowhead markers with multiple lines that are different colors and make it so the arrowheads always have the same color as the lines to which they are added? I can’t specify the fill color in the marker definition since I want to reuse the same definitions with lines that are different colors.
We’ll never get an answer, because markers as they are implemented now are absolutely useless. Timur’s question is correct: it points to a real long time problem with markers.
How can i draw the left and right arrow heads (only heads, no triangle) onto a horizontal line. I already done as per your guidance but not able to remove the triangles. Please give some pointers for the same.
Thanks
I’m not entirely sure I’m following what you’re trying to do. The triangle is the arrow head. You can use other shapes besides a triangle if you want
You might want to look at this post on creating lines and shapes with SVG.
I have a line with an end marker. The line has non-scaling-stroke effect and when I change the viewbox value of the svg the marker size changes insted of being relative to the stroke-width. Do you know how to do this?
Hi Paco.
I don’t think the marker is ever relative to the stroke-width. It will change with the viewBox like you saw.
Do you have the code online somewhere? If not you can email it to me and I’ll be happy to take a look when I have a free moment.
Going back to the first comment/question from “Timur Junussov” — how can I set the fill color for the arrow to be the same at the line which references it?
You just set them to be the same color. There’s no make this color the same as that color. If you want them both to be red, you set both to be red.
Maybe this will change, eventually,
like in Figure 4.
right above this link
[Chapter 12: Painting: Filling, Stroking and Marker Symbols](https://svgwg.org/svg2-draft/painting.html#ColorProperty)
stroke=”context-stroke”
Thanks for this tuto very clear
Shouldn’t “refx” and “refy” be “refX” and “refY”?
Thanks for the great tutorial.
Thanks. Renzi. All fixed.
Great tutorial, thank you.
However, I note in your second listing that the marker definition with id=”circle” is listed as outside the element, when it should be within it.
Styling markers is a major PITA tho. Unless context-stroke and contex-fill will roll out I’ll simply reuse defs. I’d rather append and position arrowheads by hand, than insert new style rule each time I need different colour.
Didn’t seem that bad to me, though I can understand if you prefer to reuse defs.