如何创建SVG箭头和polymarker——`marker`元素

发布于 彦子

一个常见的使用SVG绘制的图形是箭头。一行代码就可以创建一个,但是这样代码重复度很大。你也可以在<defs><symbol>中定义好再去重用,但是你每次应用的时候都需要对其移动或旋转。直接用一个<marker>元素的话会方便很多。

前面几周的时候我讲过如何组织SVG代码,以及如何定义一个可重用的图形对象。可以翻一下w3cplus上SVG系列文章进行学习~

今天我想要讨论一个特殊的元素,可以在一个地方定义,然后在另一个地方引用的——marker,通常用来创建箭头和一些polymarker。

<marker>元素

marker是一种可以连结一个或多个pathlinepolyline、或polygon的顶点的标志类型。最常见的用例是绘制箭头或在输出结果的线上的标记一个(polymarker)图形。

使用<marker>元素创建一个marker,以及其相关属性。通常我们把marker放在<defs>元素中,然后在其它地方对其进行引用。下面我们通过一个简单的实例来学习。

<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>

这个SVG包含了一个marker,以及一条引用marker的基础图形line。我们先来看看marker标签中的内容。它是包含在<defs>标签中的,所以它暂时不会被渲染出来。还赋了一个idarrow

marker标签内是path,创建了一个红色的小三角形。前面我们介绍过line路径命令,这条path绘制的就是从(0,0)点开始,绘制一条直线到点(0,6),然后再画一条线到(9,3),然后z命令关闭路径,也就是画一条线回到点(0,0)。路径使用了#f00即红色填充。

我们先忽略marker中的内容(除了id="arrow")的属性,看看line元素。这是一条黑色(#000)的水平线,宽度为5px,从点(50,50)连到点(250,50)

line元素的结尾,有一个属性叫marker-end,通过"url(#arrow)"引用了前面定义的marker。结果如下。

我们先不管marker,改变line的内容,换一个相反的方向,并和水平方向成一个小角度。

<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>

第二条线从点(295,50)开始,连到点(95,75)。我只改变了线条的xy的值。没有改动marker本身的任何东西。

marker的特殊之处就在于它们可以根据应用的对象指向的方向来调整方向。没有对<marker>中的内容做任何调整,箭头就自动调整为和line同一方向了。

marker的属性

现在我们来看看marker元素都有哪些属性吧。这是第一个实例的代码。

<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>

除了arrow这个idmarker还有六个属性在这里写出来了。

**markerWidthmarkerHeight**属性定义了marker视窗的宽度和高度。marker在自己的视窗中展示,所以这个尺寸必须至少大于marker标签内定义的图形(除非你是要隐藏图形的某部分)。

上面的实例中我把markerWidthmarkerHeight都设置为10px。path中绘制出的三角形需要适应9px x 6px的面积,所以我也可以把markerWidth设置为9,然互markerHeight设置为6。这是marker可以接受的最小尺寸,任何小于这个的尺寸都会导致图形被裁剪。

接下来的两个属性,refXrefY,指的是图形元素和marker连接的位置坐标。我们还给背后的场景应用了一个变换,来移动marker,与之对齐。

在实例中,我把三角形向上移动,通过把refY的值设置为3。将点(0,3)作为和图形元素连接的点。

下一个属性,orient,这个属性是我为什么在转换line的方向时,不需要调整marker的原因。它接受一个auto值,或者一个角度值,这个值决定了marker是否要旋转,在与其它内容连接的时候。

auto这个值表示marker会随着应用的元素一起旋转。45deg这个值则表示marker的方向一直保持45deg,不会随着连接的元素一起旋转。大多数时候这个值都是设置为auto的。

为了作对比,我们这里第二个示例设置了orient等于45deg。注意两个实例中的箭头都旋转了相同的角度。在第二个实例中它甚至被SVG视窗裁剪了,也就是它超出边框了。

最后一个属性是markerUNits,用于确定marker是否进行缩放。它定义了markerWidthmarkerHeight,以及marker的内容本身的坐标系统。

它接受两个值,strokeWidthuserSpaceOnUse。默认值是strokeWidth,这也是大家大多数情况下会设置的值,因为它允许你的marker随着它连接的line进行缩放。

strokeWidth:坐标系统中的marker值和当前描边宽度的单位是相同的尺寸。也就是说strokeWidth这个值允许你的marker缩放。

userSpaceOnUse: marker的值是当前用户坐标系统的值。也就是说如果你的marker是一个半径为10px的圆,它就一直都是10px的半径,不受连接的元素的影响。

在这篇文章前面的那个实例中,我使用的是strokeWidth。创建了一个水平9px、垂直6px三角形,然后把它和一个stroke-width5pxline连接起来。如果你量了结果SVG中的三角形的尺寸,你会发现它的尺寸是45px30px

下面这个是同一个SVG实例,只是把markerUnits的值修改成userSpaceOnUse。它的矩形就是9px6px

有时候你可能会希望marker一直保持相同的尺寸,不过大家一般还是用strokeWidth这个值,来让marker随着连接的元素进行缩放。

上周我提到了你可以给symbol元素添加一个viewBox。同样的你也可以给marker元素添加一个viewBox,作额外的缩放。例如,这里我把markerUnits的值改回成strokeWidth,然后给marker元素添加一个viewBox。(关于viewBox,欢迎戳这里学习~)

<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>

viewBox的值设为(0 0 20 20),是当前marker视窗(宽度为10,高度为10)两倍的大小,所以它会把三角形变成原来的一半大(注意,是一半!因为视窗变大啦)。结果如下。

<symbol>元素添加viewBox也是同样的道理,symbol中的所有内容都会继承这个效果。

Marker特性——在元素中引用marker

这里我所有引用marker的实例都是用的同一种方法,设置id然后赋给marker-end属性。marker-end属性是marker的一个属性,表示在哪里连接marker

marker-end="url(#arrow)"

linepathpolylinepolygon这些基础图形应用marker一共有四种方法:

  • marker-start=”url(#marker-id)”
  • marker-mid=”url(#marker-id)”
  • marker-end=”url(#marker-id)”
  • marker=”url(#marker-id)”

**注意:**你还可以在你的CSS中这样设置marker-end: url("#arrow");~

你应该可以理解marker-endmarker-start都是啥叻。这是使用marker-start的例子:

注意,箭头和之前一样都是指向一个方向。它不会改变方向,因为它指向的是line的终点。

你可能会想marker-mid是表示把marker放置在line的中间,但是不是哒。实际上如果我们把marker连接到一个line元素的marker-mid属性,是没有任何效果的。marker完全没有出现。

marker-mid属性设置在当polylinepathpolygon转换方向的位置点。这里我创建了另一个marker,里面放个圆形。我把三角形marker放到了polylinelinepath的尾部,然后把圆形marker放到了markerStartmarkerMid

<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>

结果如下。有三个圆重叠出现在了linepolylinepath的起点位置。你只能看到最顶上的那个圆。

还有line(向右下45deg)并没有一个mid圆。而polylineline在改变方向的地方都有一个圆,就是marker-mid

marker属性(没有-start-mid-end)是其它三个简写,不过我没有发现它在哪里可以工作。

总结

marker是给图形元素(如linepathpolylinepolygon)添加内容的好方法,通常用来做箭头和连接点(如最后一个实例中的圆)。

你可以使用少量的属性来控制你的marker的尺寸和位置。还可以控制它是随着连接的内容一起缩放,或保持绝对尺寸。另外,四个marker(-start,-mid,-end)属性允许你将marker连接到linepolylinepolygonpath的不同位置。

下次我们要讨论另一个元素了,用来定义图形元素的。pattern元素,以及如何创建图案,填充到不同的元素上。

给大家找了几个codepen上关于marker的实例,巩固学习~


本文根据@Steven Bradley的《How To Create SVG Arrowheads and Polymarkers — The marker Element》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://vanseodesign.com/web-design/svg-markers/

彦子

在校学生,本科计算机专业。逗比一枚,热爱前端热爱生活,喜欢CSS喜欢JavaScript喜欢SVG,爱玩PS玩AI玩啊逗比的软件。努力向上,厚积薄发。

如需转载,烦请注明出处:https://www.fedev.cn/svg/svg-markers.htmlair max 90 essential green