SVG奇淫巧技(五):你只管绘制图案,其余交给marker就好
我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第5篇文章。
很早之前做过一个项目,是用SVG
去实现节点之间的关系,并且连线上要有箭头表示数据的走向,当时对于箭头的坐标计算着实费了一番功夫。
本以为当时的方法虽然麻烦但应该算是标准方案了,直到重新学习SVG
看到了<marker>
元素,才发现当初的方案纯属是力大砖飞的莽夫行为罢了。
可以绘制箭头的<marker>
老规矩先看下MDN上关于<marker>
的定义:
marker
元素定义了在特定的<path>
、<line>
、<polyline>
或者<polygon>
上绘制的箭头或者多边标记图形。
从定义上可以看出<marker>
主要就是用于绘制箭头或其他图案而存在的,还是看图比较直观:
<svg width="200" height="200" viewBox="0 0 100 100">
<defs>
<!-- 箭头标记 -->
<marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5"
markerWidth="6" markerHeight="6"
orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<!-- 单点标记 -->
<marker id="dot" viewBox="0 0 10 10" refX="5" refY="5"
markerWidth="5" markerHeight="5">
<circle cx="5" cy="5" r="5" fill="red" />
</marker>
</defs>
<!-- 坐标轴绘制 -->
<polyline points="10,10 10,90 90,90" fill="none" stroke="black"
marker-start="url(#arrow)" marker-end="url(#arrow)" />
<!-- line图绘制 -->
<polyline points="15,80 29,50 43,60 57,30 71,40 85,15" fill="none" stroke="grey" marker-start="url(#dot)" marker-mid="url(#dot)" marker-end="url(#dot)" />
</svg>
从代码中,我们可以看到 <marker>
是支持 viewBox
属性的,那么同样的它也是支持preserveAspectRatio
属性的,原理与 <svg>
相同。
除此之外,markerWidth
和 markerHeight
用于定义图案的宽高,这很好理解。
refX
和 refY
用于定义图案的标记点坐标,至于标记点嘛,你可以简单理解为线段的终端在图案中的位置。
除此之外,线段上还可以设置 marker-start
、 marker-mid
和 marker-end
这3个属性,通过名字就可以看出,是用来标识图案在线段上出现的位置,值得注意的是marker-mid
属性在仅有单条线段时无效。
以上的属性都很好理解,那么就只剩下一根难啃的骨头了——orient
,这个属性代表朝向,顾名思义就是用来定义图案朝向的,它有4个可选值:
auto: 默认值,它显示了标记的方向,使其正
x
轴指向相对于放置标记的位置的路径的方向;auto-start-reverse: 此属性与
marker-start
一起使用,则标记的方向与指定auto
时所使用的方向相差180°
;angle: 指定的角度是形状的正
x
轴与标记之间的距离;number: 它显示以度为单位的角度。
话不多说,直接上图:
<svg width="200" height="200" viewBox="0 0 100 100">
<defs>
<!-- arrowhead auto -->
<marker id="arrow-auto" viewBox="0 0 10 10" refX="5" refY="5"
markerWidth="6" markerHeight="6"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<!-- arrowhead auto-start-revers -->
<marker id="arrow-reverse" viewBox="0 0 10 10" refX="5" refY="5"
markerWidth="6" markerHeight="6"
orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<!-- arrowhead angle -->
<marker id="arrow-angle" viewBox="0 0 10 10" refX="5" refY="5"
markerWidth="6" markerHeight="6"
orient="90deg">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<!-- arrowhead number -->
<marker id="arrow-number" viewBox="0 0 10 10" refX="5" refY="5"
markerWidth="6" markerHeight="6"
orient="90">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
</defs>
<line x1="10" y1="20" x2="10" y2="80" stroke="black" marker-start="url(#arrow-auto)" marker-end="url(#arrow-auto)" />
<line x1="35" y1="20" x2="35" y2="80" stroke="black" marker-start="url(#arrow-reverse)" marker-end="url(#arrow-reverse)" />
<line x1="60" y1="20" x2="60" y2="80" stroke="black" marker-start="url(#arrow-angle)" marker-end="url(#arrow-angle)" />
<line x1="85" y1="20" x2="85" y2="80" stroke="black" marker-start="url(#arrow-number)" marker-end="url(#arrow-number)" />
</svg>
可以看到,auto
和 auto-start-reverse
只是在marker-start
时表现为朝向的区别,而angle
和 number
其实是等价的,只是写法不一样,从代码上可以很清楚的看到这一点。
只是,以上案例中,都是实心的箭头和圆点,那如果是空心的是否还适用呢?
答案是可以,但是不完美,箭头还可以通过调整refX
和 refY
让线段不再超出到箭头内部。但是通过marker-mid
来实现的图案就只能爱莫能助了。
所以,<marker>
虽好,但也只相对于通过fill
填充的图案,对于非填充的图案就显得捉襟见肘了。
OK,以上就是关于<marker>
的知识啦,不过,凡事都怕琢磨,不知道是否有聪明的小伙伴会有疑问,如果是多起点的<path>
路径<marker>
的表现又如何呢?
多个起点M
的<path>
首先,我们要知道<path>
是可以支持多个M
起点的,其实就相当于画画的时候不一定非要一笔画下来,可以很多笔画一个图案,例如:
<svg width="200" height="200" viewBox="0 0 100 100">
<path d="M 10 20 V 80
M 35 20 V 80
M 60 20 V 80
M 85 20 V 80"
stroke="black" />
</svg>
但是,如果结合<marker>
的话,并不是每个起点都算是start
,还是按照代码顺序决定的,例如:
<path d="M 10 20 V 80
M 35 20 V 80
M 60 20 V 80
M 85 20 V 80"
marker-start="url(#arrow-reverse)"
marker-end="url(#arrow-reverse)"
stroke="black" />
不过,这时如果加上 marker-mid
其他起点也会生成marker
只是arrow-reverse
属性不再对它们生效:
另外,值得注意一点,虽然<path>
支持多起点,我们也可以通过多个起点来组成一个图形例如矩形,不过理论上它们仍属于一条条独立的线段,只是在视觉上让他们看起来围在了一起,但这并不算闭合图形,所以这样实现的图形是无法被fill
填充的,也无法被z
属性闭合,比如:
<svg width="200" height="200" viewBox="0 0 100 100">
<path d="M 10 20 V 80 M 10 80 H 80 M 80 80 V 20 M 80 20 H 10 Z" stroke="black" fill="red" />
</svg>
好啦,看到这里想来如此好用的<marker>
大家已经清楚了,下次再用SVG
实现类似含有箭头的线段时记得学以致用哈,提前祝大家中秋快乐,就是这样再见吧。
转载自:https://juejin.cn/post/7141376633465733150