Re-purposing Icons as Markers
In the book, we discuss how the viewBox
attribute on the <marker>
element can be used to change the position of the marker’s origin, and therefore of the point that gets aligned with the exact vertex (corner-point) of the marked shape.
A marker’s viewBox
can also be used in the same way as any other SVG viewBox
, to create a local scaled coordinate system. This is particularly useful when reusing icons that you already have, such as in a <symbol>
definition.
However, pre-defined symbol icons aren’t likely to have been defined in such a way that the origin is at meaningful point for creating a marker. You can change the reference point of the <marker>
element, separately from the viewBox
origin, using refX
and refY
.
In sum, to convert a <symbol>
to a marker:
-
The
viewBox
on your<marker>
should match theviewBox
from the original<symbol>
. -
The
refX
andrefY
values should match the “pin point” of your icon—the point that you want to align with the corner or cap of the path. The values are measured in the scaledviewBox
coordinate system.
Figure 14-X1 shows the result of adapting our card-suit heart icon to create a Valentine worthy of a besotted 11-year-old. Heart-shaped markers are positioned around a heart shape.
For reference, this is the <symbol>
version of the icon, from Chapter 10:
<symbol
viewBox=
"0 0 20 20"
id=
"heart"
style=
"overflow: visible"
>
<title>
Heart</title>
<path
d=
"M10,6 Q10,0 15,0T20,6Q20,10 15,14
T10,20Q10,18 5,14T0,6Q0,0 5,0T10,6Z"
/>
</symbol>
The viewBox
of the marker will be set to the same 20×20 scale, with a top-left origin, that we used for the original icon. But the top-left corner isn’t a good reference point for the marker, since it is outside of the heart shape. Instead, we’ll use refX
and refY
to set the reference point to the center of the icon.
You’ll usually also want to define the marker’s dimensions, using markerWidth
and markerHeight
; by default, your viewBox
will scale to fit a square three times as wide as the stroke on the shape you’re marking.
In Figure 14-X1, the markers are scaled to fit within a thick stroke on the shape. With the default strokeWidth
value for markerUnits
, that means that markerWidth
and markerHeight
are slightly less than 1.
Example 14-X1 gives the code.
Example 14-X1. Using icons as markers
<svg
xmlns=
"http://www.w3.org/2000/svg"
xml:lang=
"en"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"250px"
height=
"250px"
viewBox=
"-2 -2 24 24"
>
<title>
Heart-shaped Markers on a Heart Shape</title>
<marker
id=
"heart-marker"
viewBox=
"0 0 20 20"
markerHeight=
"0.9"
markerWidth=
"0.9"
orient=
"auto"
refX=
"10"
refY=
"10"
fill=
"darkRed"
stroke=
"none"
>
<path
id=
"heart"
d=
"M10,6 Q10,0 15,0T20,6Q20,10 15,14
T10,20Q10,18 5,14T0,6Q0,0 5,0T10,6Z"
/>
</marker>
<use
xlink:href=
"#heart"
stroke-width=
"4"
fill=
"indianRed"
stroke=
"lightCoral"
marker-mid=
"url(#heart-marker)"
/>
</svg>
When reusing shapes like this, be careful not to create infinite loops: if the <marker>
contains any content that is styled to use the same marker, it won’t be drawn at all. This is why Example 14-X1 defined the shape within the <marker>
and reused it in the main drawing, instead of the reverse.
Warning
Microsoft Edge and Internet Explorer’s loop error handling is overly strict. Neither browser will draw the markers in Example 14-X1 as it is written, or even if the heart path is defined separately (in a <defs>
) and then reused in both the marker and the main figure.
In order to get the markers to be drawn in Microsoft browsers, the marker and the shape it is marking need to be completely independent. In other words, for Example 14-X1, you need to copy & past the <path>
element in the <marker>
separate from the one that is used (or reused) to draw the main shape. The result would be the same as Figure 14-X1 in other browsers; Figure 14-X2 shows the result in MS Edge.
Figure 14-X1 (from Firefox) and Figure 14-X2 (from MS Edge) should serve as one final warning about the complexity of stroke and marker styling: some of the markers end up with different orientations.
The stroke line joins at the inside and outside points of the shape are both 180° U-turns. As a result, the strokes at both points are both cropped flat against the point of the shape. The markers centered over those points protrude outside the stroke. And the two browser renderings don’t agree on their orientations.
The fact there is a marker at all on the inner point—which is where the start and end of the <path>
meet—is somewhat unexpected, because the markers were assigned with the marker-mid
presentation attribute. The start and end points should therefore not be marked.
The marker that is drawn at that point is for the line join between the final curve and the zero-length line created by the Z
close path command.
Tip
This unexpected marker is one of the main reasons that SVG 2 introduces a new Z
syntax to define a curve that automatically closes the path. Unfortunately, that new syntax is not yet supported in browsers.
The different browsers orient this final marker differently. A zero-length line does not have a defined angle, and the browsers differ in how they determine the orientation in that case. SVG 2 clarifies that the browser should skip over zero-length lines until it reaches a line with a valid stroke angle, but the browsers have not yet all caught up.
The bottom point doesn’t have a zero-length line. But it does have two vertical tangent angles, for the incoming and outgoing curves. Half-way between those two vertical lines is going to be a horizontal line, but the browsers can’t decide whether it is facing up or facing down.