SVG中遇到的问题(一)

1,188 阅读3分钟

前言:最近在工作中经常使用svg,记录一下遇到的问题。

一、给连接线加箭头

两个对象之间是有向连接的关系,比如一级部门与二级部门,上级与下级。我们怎么实现给连接线加上箭头?如果对象的位置是固定的,那么可以计算出箭头的位置。如果对象是可拖拽的,位置不固定,那么计算就复杂了。svg的<marker>可以帮你轻松实现这个功能。

1.marker是什么

顾名思义,marker就是标记的意思。标记可以在图形的起点、终点和交点,通过marker-startmarker-midmarker-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,还可以根据需要设为成角度,比如30deg
  • markerUnits 设置标记的坐标系。设置为strokeWidth,标记里的数字单位等于图形的stroke-width值,如图中的两条直线分别设置stroke-width等于2和1,标记的大小就不一样。如果设置为userSpaceOnUse,标记大小将不变。
  • refXrefY与图形对齐的标记坐标点。上图中设置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是居中于路径绘制的,分成innerStrokeouterStroke,宽度都是0.5px。图中一半的outerStrokeviewBox区域外,因此看不到。如果增大一倍边框,会是下面这样

边框宽度1px,圆角宽度2px,这样也是不行的。

2.解决方案

最好的解决方案自然是SVG本身支持设置stroke对齐方式。在SVG2起草规范中加入了

stroke-alignment就是用来解决这个问题的,文档链接。可惜后来规范又移除了

stroke-alignment,所以只能用其他方案解决了。下面是三个解决方案:

2.1 移动矩形坐标

xy偏移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裁剪实现

边框宽度设置为两倍大,innerStrokeouterStroke都是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>

参考资料

developer.mozilla.org/en-US/docs/…

juejin.cn/post/684490…

stackoverflow.com/questions/7…