Tuesday, 5 May 2015

How To Create SVG Arrowheads and Polymarkers — The marker Element - Vanseo Design

How To Create SVG Arrowheads and Polymarkers — The marker Element - Vanseo Design


How To Create SVG Arrowheads and Polymarkers — The marker Element

Posted: 04 May 2015 05:30 AM PDT

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.

1  2  3  4  5  6  7  8  9  
<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>

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.

1  2  3  4  5  6  7  8  9  
<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>

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.

1  2  3  4  5  6  7  8  9  
<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>

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.

1  2  3  4  5  6  7  8  9  
<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>

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.

1  
marker-end="url(#arrow)"

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.

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  
<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>

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.

The post How To Create SVG Arrowheads and Polymarkers — The marker Element appeared first on Vanseo Design.

This posting includes an audio/video/photo media file: Download Now