前言:最近在工作中经常使用svg,记录一下遇到的问题。
一、给连接线加箭头
两个对象之间是有向连接的关系,比如一级部门与二级部门,上级与下级。我们怎么实现给连接线加上箭头?如果对象的位置是固定的,那么可以计算出箭头的位置。如果对象是可拖拽的,位置不固定,那么计算就复杂了。svg的<marker>可以帮你轻松实现这个功能。
1.marker是什么
顾名思义,marker就是标记的意思。标记可以在图形的起点、终点和交点,通过marker-start,marker-mid,marker-end设置标记的位置。
箭头一般加在图形的起点或者终点,下面分别给曲线、折线和直线加上终点标记。
箭头在终点
<svg width="400" height="60" viewBox="0 0 400 60" xmlns="http://www.w3.org/2000/svg"> <defs> <marker id="triangle" markerUnits="strokeWidth" markerWidth="8" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M 0 0 L 8 4 L 0 8 z" fill='red'/> </marker> </defs> <path d="M 20,10 C 50,10 50,40 80,40" stroke="black" stroke-width="2" fill="none" marker-end="url(#triangle)"/> <path d="M 100,30 L 150,50 L 180,20" stroke="black" stroke-width="2" fill="none" marker-end="url(#triangle)" /> <line x1="200" y1="30" x2="250" y2="30" stroke="black" stroke-width="2" fill="none" marker-end="url(#triangle)" /> <line x1="200" y1="50" x2="250" y2="50" stroke="black" stroke-width="1" fill="none" marker-end="url(#triangle)" /> </svg>
在<defs>中定义<marker>。因为svg没有三角形元素,我们可以用<path>绘制一个三角形。
2.marker的属性
markerWidth标记的宽度markerHeight标记的高度orient标记相对与图形的方向,终点箭头标记设为auto,起点箭头标记设为auto-start-reverse,还可以根据需要设为成角度,比如30degmarkerUnits设置标记的坐标系。设置为strokeWidth,标记里的数字单位等于图形的stroke-width值,如图中的两条直线分别设置stroke-width等于2和1,标记的大小就不一样。如果设置为userSpaceOnUse,标记大小将不变。refX,refY与图形对齐的标记坐标点。上图中设置refX="0" refY="4",三角形一边与路径终点居中对齐。如果设置refX="0" refY="0",那么三角形的上顶点与路径终点对齐。
3.其他示例
箭头在起点
<svg width="400" height="60" viewBox="0 0 400 60" xmlns="http://www.w3.org/2000/svg"> <defs> <marker id="triangle2" markerUnits="strokeWidth" markerWidth="8" markerHeight="8" refX="0" refY="4" orient="auto-start-reverse"> <path d="M 0 0 L 8 4 L 0 8 z" fill='red'/> </marker> </defs> <path d="M 20,10 C 50,10 50,40 80,40" stroke="black" stroke-width="2" fill="none" marker-start="url(#triangle2)"/> <path d="M 100,30 L 150,50 L 180,20" stroke="black" stroke-width="2" fill="none" marker-start="url(#triangle2)" /> <line x1="200" y1="30" x2="250" y2="30" stroke="black" stroke-width="2" fill="none" marker-start="url(#triangle2)" /> <line x1="200" y1="50" x2="250" y2="50" stroke="black" stroke-width="1" fill="none" marker-start="url(#triangle2)" /> </svg>
标记在交点
<svg width="400" height="100" viewBox="0 0 400 100" xmlns="http://www.w3.org/2000/svg"> <defs> <marker id="triangle2" markerUnits="strokeWidth" markerWidth="8" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M 0 0 L 8 4 L 0 8 z" fill='red'/> </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> <path d="M 20,10 C 50,10 50,40 80,40" stroke="black" stroke-width="2" fill="none" marker-mid="url(#dot)"/> <path d="M 100,30 L 150,50 L 180,20" stroke="black" stroke-width="2" fill="none" marker-mid="url(#dot)" /> <line x1="200" y1="30" x2="250" y2="30" stroke="black" stroke-width="2" fill="none" marker-mid="url(#dot)" /> <line x1="200" y1="50" x2="250" y2="50" stroke="black" stroke-width="1" fill="none" marker-mid="url(#dot)" /> <polyline points="15,90 29,80 43,70 57,90 71,80 85,100" stroke-width="2" fill="none" stroke="black" marker-mid="url(#dot)"/> </svg>
交点是线与线相交之处,所以曲线、直线、矩形、圆、椭圆是没有交点的,也就没有标记。
二、stroke-width少了一半的宽度?
1.问题描述
绘制一个矩形,边框1px,圆角2px。结果边框宽度是0.5px,如下图所示。图中参考横线宽度1px。
<svg width="60px" height="100px" viewBox="0 0 60 100"> <rect x="0" y="0" width="60" height="100" stroke="black" stroke-width="1" rx="10" ry="10" fill="none"/> <line stroke="black" x1="0" y1="20" x2="60" y2="20"/> </svg>
这是因为stroke是居中于路径绘制的,分成innerStroke和outerStroke,宽度都是0.5px。图中一半的outerStroke在viewBox区域外,因此看不到。如果增大一倍边框,会是下面这样
边框宽度1px,圆角宽度2px,这样也是不行的。
2.解决方案
最好的解决方案自然是SVG本身支持设置stroke对齐方式。在SVG2起草规范中加入了
stroke-alignment就是用来解决这个问题的,文档链接。可惜后来规范又移除了
stroke-alignment,所以只能用其他方案解决了。下面是三个解决方案:
2.1 移动矩形坐标
把x,y偏移strokeWidth/2,宽度和高度分别减少一个strokeWidth
<svg width="60px" height="100px" viewBox="0 0 60 100"> <rect x="0.5" y="0.5" width="59" height="99" stroke="black" stroke-width="1" rx="10" ry="10" fill="none"/> <line stroke="black" x1="0" y1="20" x2="60" y2="20"/> </svg>
边框宽度正常了
2.2 通过改变viewBox
viewBox向外扩大strokeWidth/2
<svg width="60px" height="100px" viewBox="-0.5 -0.5 61 101"> <rect x="0" y="0" width="60" height="100" stroke="black" stroke-width="1" rx="10" ry="10" fill="none"/> <line stroke="black" x1="0" y1="20" x2="60" y2="20"/> </svg>
2.3 通过clip-path裁剪实现
边框宽度设置为两倍大,innerStroke和outerStroke都是1px,然后裁剪掉图形外的部分,也就是outerStroke。这样看到的边框就是1px。
<svg width="60" height="100" viewBox="0 0 60 100" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <rect id="ld" x="0" y="0" width="60" height="100" rx="10" ry="10" fill="none"/> <!-- <path id="ld" d="M0,0 L60,0 L60,100 L0,100 Z"/> --> <clipPath id="clip"> <use xlink:href="#ld"/> </clipPath> </defs> <g> <use xlink:href="#ld" stroke="black" stroke-width="2" fill="#fff" clip-path="url(#clip)"/> </g> </svg>