初尝 SVG 之 Path

3,619 阅读10分钟

前言

所谓视频看千百遍,不如自己实践。以前看慕课网的SVG视频好多,但实际到项目中还是得重新翻起SVG教程来学习。此次实践是充分使用了SVG的Path指令,所以在这里详细讲解一下Path的用法。

1、demo1介绍

我们依然采用结合代码的方式来了解SVG,首先上场的是第一个demo:使用SVG画出一个定位符号,效果如下:

在该demo中我们结合高德地图,在指定的经纬度上画出了一个定位符号,而不是使用高德地图提供的默认定位符。读取代码可以知道该定位符号的svg代码是:

<svg width="30" height="50">
 <circle cx="17" cy="15" r="5" fill="transparent" stroke="#f44336"/>
 <path d="M4,15 a 1,1 0, 1, 1, 25 0 L 15 50 L 4 15" fill="transparent" stroke="#f44336" stroke-width="2"/>
</svg>

围绕这段代码我们展开解释。

1.1、SVG简单介绍

用过SVG的都会将其和canvas进行比较,二者的区别不再赘述(可以参考Differences Between SVG and Canvas),我们援引W3Schools中对SVG的介绍:

SVG代表可伸缩的矢量图形(Scalable Vector Graphics)
SVG定义了使用XML格式的基于矢量的图形
SVG的优点:
  • SVG图片可以使用任何文本编辑器进行创建和编辑
  • SVG图片可以被搜索、索引、脚本化以及压缩
  • SVG图片可伸缩
  • SVG图片可以以任何一种分辨率进行高质量打印
  • SVG图片可以缩放
  • SVG是一种开源的标准
  • SVG文件是纯XML
SVG的使用:
  • 直接嵌入到HTML页面中
  • 将SVG作为一个<img>来使用
  • 将SVG作为一个background-image来使用
  • 将SVG作为一个<object>来使用
  • 将SVG作为一个Data URI's来使用

以上用法具体可以参考:Using SVG

1.2、SVG语法

一个SVG以<svg>标签开始,并以</svg>结束,在svg标签上有几个特有的属性,常用的有:width、height、viewBox等,其中viewBox属性允许你指定一组给定的图形位置组合(min-x, min-y, widthheight)以符合一个特定的的容器元素。更多用法参考 SVG

1.3、定位符的制作

我们的定位符号比较简单,有一个半圆,一个小圆形和两条线段组成。于是我们先画出一个小圆:

<circle cx="17" cy="15" r="5" fill="transparent" stroke="#f44336"/>

其中用到SVG的<circle>语法,使用SVG画一个圆那是分分钟的事情,只需要你填写cxcyr这3个属性,那么一个圆形就立马可得。后面的两个参数:fill表示圆形需要填充什么颜色,stroke表示圆形的线条使用什么颜色,后面还有诸如stroke-widthfill-opacity等类似公共的属性。

接下去开始介绍我们这篇博文的重点:Path

1.4、Path

<path>号称是SVG库中基本图形中最有魔力的一个元素,你可以使用它去画线段、椭圆、圆形等等。

学习path之前我们明白在path指令中,命令的大小写是有区别的:大写表示后面跟的坐标是绝对坐标,小写则是相对坐标。

我们所画的轨迹命令都是属于d属性的,在d属性中包含下面5中路径描述:

  • MoveTo(缩写成M或m)
  • LineTo(缩写成L或l)
  • CurveTo(缩写成C或c)
  • ArcTo(缩写成A或a)
  • ClosePath(缩写成Z或z)

我们先来写一个小demo,让大家对相对坐标和绝对坐标有个感性的认识:

上面代码中的第一个SVG的LineTo命令使用小写的l,表示相对坐标,我们先MoveTo绝对坐标(0, 0),其实这会即使使用相对坐标也是一样的,然后画一根线,这根线使用相对坐标,也就是相对于Move之后的点(0, 0),先基于(0, 0)左移15px,然后再下移50px,到第2个点,接着继续使用相对坐标,继续左移30px,下移0px,得到第3个点,效果如上图。

然后在第2个SVG中使用绝对坐标,最后一个点就完全不一样了,因为给定的是最后点应该落在(30, 0)上面,所以效果如图所示。

Path的MLZ这三个命令比较简单,就不赘述了。值得一提的有两种简写的形式去画出一条垂直或者水平的直线:

H x (or h dx)
V y (or v dy)

接下来介绍两个比较复杂的命令:CA

1.4.1、Curveto

Curveto命令用于绘出贝塞尔曲线,有两种贝塞尔曲线:三次方和二次方,二次方曲线是一种特殊的三次方贝塞尔曲线。

首先二次方贝赛尔曲线只需要一个控制点就可以决定起点和终点之间的斜率,所以其语法是:

Q x1 y1, x y (or q dx1 dy1, dx dy)

如下面的代码demo,为了方便看出控制点的作用,我们在每个SVG后面都会画出控制点与起点和终点的辅助线段。

如果你需要画出多个二次方贝赛尔曲线,那么可以使用T命令来实现,如上面代码中的第二个SVG

三次方贝塞尔曲线比二次方贝赛尔曲线多了一个控制点,因此语法如下: C x1 y1, x2 y2, x y (or c dx1 dy1, dx2 dy2, dx dy)

和二次方类似,如果需要画多个贝赛尔曲线的话,使用S命令,参考demo代码中的第3和第4段SVG代码。

这里读者也许会发现当我使用S或者T命令的时候,在使用辅助线描绘控制点线段的时候我是如何知道第二段贝赛尔曲线的控制点的坐标的?答案其实很简单,因为这两个命令创建出来的第二段贝塞尔曲线与第一段贝塞尔曲线的斜率必须是一样的,所以其控制点其实是从第一段的控制点基于第一段终点为镜像点,做一个镜像即可,比如:

 <path d="M10 80 Q 55 10 100 60 T 180 60" fill="transparent" stroke="#f44336" stroke-width="2"/>

那么第二段的控制点坐标是(55,10)基于(100,60)点对称,所以计算得到X轴坐标应为:100 - 55 + 100 = 145, Y轴坐标应为: 60 - 10 + 60 = 110,所以有:(145, 110)这一个点的坐标

 <path d="M10 80 L 55 10 L 100 60 L 145 110 L 180 60" fill="transparent" stroke="#f44336" stroke-width="1"/>
1.4.2、Arcsto

这个命令也是可以画出曲线,包括圆形、椭圆等。其语法是:

 A rx ry x-axis-rotation large-arc-flag sweep-flag x y
 a rx ry x-axis-rotation large-arc-flag sweep-flag dx dy

解释一下上面参数的含义:

  • rx:表示椭圆的X轴长度半径,当该值与ry一样的时候表示画出一个圆
  • ry: 表示椭圆的Y轴长度半径
  • x-axis-rotation:基于X轴的旋转角度
  • large-arc-flag:决定画出的arc是否应该是大于或者小于180度,也就是顺时针还是逆时针的问题
  • sweep-flag:决定是否画出的arc应该从正的角度还是负的角度开始移动,通俗点说就是让你决定你在两个相交的圆中取哪一个圆的图形
  • x:表示终点的X轴坐标
  • y:表示终点的Y轴坐标

关于sweep-flag的解释,可以看一下这个demo:

很直观地可以看出sweep-flag的值影响到了你在两个辅助圆中取哪一个圆的曲线,蓝色标记的弧线便是所取的曲线

我们基于上面的小demo,给出下面几种组合的代码:

1.5、demo1的path命令解释

我们的定位符除掉中心的圆圈之外有一个半圆以及两条线段,代码是:

<path d="M4,15 a 1,1 0, 1, 1, 25 0 L 15 50 L 4 15" fill="transparent" stroke="#f44336" stroke-width="2"/>

这里的arcsTo使用了a,所以需要注意的就是最后的两个参数是相对坐标,这个时候就会出现一个现象:

rxry的值如果小于起点到终点之间的距离,那么绘画出来的曲线将会取值起点到终点的距离,而不是rxry的值,所以可以看到即使我们配置了(1,1),但是因为终点是(29, 15),所以默认最小的rx=12.5,因此还是可以画出一个半圆的。当你把rx往大的调整,你就会发现画出的曲线不再是半圆,而是更小的一段弧线,因为毕竟你的半径在变大,终点坐标就无法在刚好半圆结束的位置,所以才会有这种效果出来。

2、demo2介绍

demo2的目标是任意给出两点坐标,绘制出两点之间的连线并且在终点有对应的箭头指向。这个demo依然是在高德地图上制作,绘制箭头并不难,比较复杂的是箭头的方向需要根据两点之间的角度进行调整,最后大致效果如下:

该demo的path部分不再赘述,说说代码中另外两个新东西:

  1. 使用了g元素
  2. 计算角度并让svg旋转

2.1、g元素

g元素是一个容器用于把SVG其他元素归为一组并共享那些公共的属性。对g元素的变换将会作用到它的所有子元素,子元素继承它的任何属性。

于是在上面的demo中我们就不再需要每个path都写上stroke-width等公共属性了。

2.2、计算轨迹角度

这一部分就是考验数学功底的时候了,给出已知两点的坐标,求出两点之间连线与水平之间的夹角,这种问题在以前初中数学天天练习,大致思想便是:

  1. 将坐标变换成具体的像素点位置
  2. 计算出X轴与Y轴两点之间距离的差
  3. 使用arctan的办法反算出弧度
  4. 再将弧度换算成角度

此时得到的角度可正可负,在使用svg的rotate的时候需要得到的角度-180才可以旋转出正确的方向。

3、结论

SVG还有好多很牛掰的特性,这里只是冰山一角,以后若是有用到其他的,依然会总结分享出来。

参考

  1. developer.mozilla.org/en/docs/Web…
  2. css-tricks.com/svg-path-sy…
  3. css-tricks.com/using-svg/
  4. lbs.amap.com/api/javascr…
评论正在加载,请稍后...

注意:如果长时间无法加载,那么说明你的网络被墙掉了,请使用代理或者VPN访问。 Please enable JavaScript to view the &amp;amp;amp;amp;lt;a href='https://disqus.com/?ref_noscript'&amp;amp;amp;amp;gt;comments powered by Disqus.&amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;gt;