上两篇我们通过对 svg
路径 M/H/V/L/C/Q/Z
几个指令的解析。把 掘金 logo
的 svg
,转化为 Flutter
的原生路径绘制,并且附加了一些绘制效果。
除了这些外,还有一些其他的指令。本篇的目的就是全面梳理一下 svg
中 path
标签下的路径命令。如下是一览表:
M/m (x,y) 移动当前位置
L/l (x,y) 直线
H/h (x) 水平线
V/v (x) 竖直线
Z/z 闭合路径
Q/q (x1,y1,x,y) 二次贝塞尔曲线
T/t (x,y) 光滑绘制二次贝塞尔曲线
C/c (x1,y1,x2,y2,x,y) 三次贝塞尔曲线
S/s (x2,y2,x,y) 光滑绘制三次贝塞尔曲线
A/a (rx,ry,xr,laf,sf,x,y) 弧线
一、绝对与相对指令
可能大家看到,每个指令都有 大写字母
和 小写字母
。其中 大写字母
表示其后的坐标是 绝对坐标
,也就是以区域 左上角
为原点的坐标。绝对和相对
坐标是绘制中最基本的概念,很容易理解。本文中的 svg
示例文件源码于 【idraw/extra_02_svg/base】 。
1. 绝对坐标移动示例
绝对移动,使用大写字母。如下第二段 M30,30
表示将起点移到 (30,30)
点上,V60
表示横坐标不变,纵坐标到绝对的 60
点。
---->[01_M.svg]----
<path d="M30,20 H80 V40Z M30,30 V60" stroke="#000082" />
2.相对坐标移动示例
而相对移动,使用小写字母。如下 m30,30
表示在以当前路径的 尾点
为参考,坐标移动了
多少。在 m30,30
之前,路径的尾点是 (30,20)
;也就说明 m30,30
会把下一段的起点移到 (30 30,20 30)
,即 (60,50)
点。 v30
表示:以当前路径的 尾点
为参考,坐标竖直移动了
多少,会直线到 (60,50 30)
,即 (60,80)
。
---->[02_m_v.svg]----
<path d="M30,20 H80 V40Z m30,30 v30" stroke="#000082" />
3.绝对坐标和相对坐标使用场景
如果能够精确的知道某点的坐标,使用 绝对坐标
自然是最方便的。但很多时候,绝对坐标并能直接获取,比如下面 A
点右移 50,下移 20,到 B
点。 虽然可以根据数据计算出 B
的绝对坐标,但比较麻烦,特别是对于一些曲线路径,相对偏移会非常方便。
---->[03_l.svg]----
<path d="M30,20 H80 V40Z m0,20 l50,20 V40 l-50,20Z" stroke="#000082" />
二、曲线路径
曲线路径包括:
二次贝塞尔曲线
,指令符为Q/q
,已及光滑形式的T/t
;三次贝塞尔曲线
,指令符为C/c
,已及光滑形式的S/s
;弧形曲线
,指令符为A/a
;
1. 二次贝塞尔曲线 Q/q
每段
二次贝塞尔曲线由两个坐标
构成,分别代表 控制点
和 结束点
。下面两段贝塞尔曲线分别通过 绝对 Q
和 相对 q
形成。比如上面一条的控制点是 70,10
,它与起点和终点的连线和曲线相切,如虚线所示:
---->[04_Qq.svg]----
<path d="M30,20 Q70,10 80,40" stroke="#000082" />
<path d="M80,40 q-40,30 -10,40" stroke="#FF743D" />
2.三次贝塞尔曲线 C/c
每段
三次贝塞尔曲线由三个坐标
构成,分别代表 控制点1
、 控制点2
和 结束点
。下面两段贝塞尔曲线分别通过 绝对 C
和 相对 c
形成。比如上面一条的控制点是 控制点1: (50,10)
、 控制点2: (80,20)
,控制点1与起点
连线、控制点2与终点
连线和曲线相切,如虚线所示:
---->[05_Cc.svg]----
<path d="M30,20 C50,10 80,20 80,40" stroke="#000082" />
<path d="M80,40 c-40,10 -50,30 -10,40" stroke="#FF743D" />
3. 弧形曲线 A/a
这个指令有 7
个参数,看着是不是有点小崩溃。在 Flutter
中,其实它就是对应 Path#arcToPoint
方法,七个参数示意如下:
下面两段弧线分别通过 绝对 A
和 相对 a
形成。弧线本质上是从 椭圆上截取弧线
,前两个值是椭圆的两个半轴长度;第四个值表示是否取大圆弧,如下实线部位取大圆弧,虚线部位取小圆弧;第五个值代表是否顺时针,如下实线部顺时针,虚线部位逆时针;第六第七值代表结束点坐标。
---->[06_Aa.svg]----
<path d="M30,20 A20 20 0 1 1 50 50 a15 20 0 1 1 20 30" stroke="#000082" />
另外单独说一下第三值,代表 旋转角度
,注意这个值是 角度值
不是 弧度值
。如下橙色是旋转 45
的效果,旋转并不是以椭圆中心旋转,而是 y
轴的倾斜角度,同时需要满足椭圆过起止点。
---->[07_Aa_rotate.svg]----
<path d="M40,50 A20 30 0 1 1 60 70 " stroke="#000082" />
<path d="M40,50 A20 30 45 1 1 60 70 " stroke="#FF743D" />
4. 光滑形三次贝塞尔曲线 S/s
每段 S
指令后面是两个坐标,但它是一个 三次贝塞尔曲线
。通过下面的例子可以看出它和 Q
的区别、与 C
的关系。在该案例中,S
表示 控制点1
和起点重合,控制点 2
是 40,70
。如下的 S
和 C
是同一条曲线:
---->[08_SQC.svg]----
<path d="M20,10 C20,10 40,70 80,50" stroke="#F619FF"/>
<path d="M20,10 S40,70 80,50" stroke="#000082"/>
<path d="M20,10 Q40,70 80,50" stroke="#FF743D"/>
另外 S
最难把握的一点是:
若 S 的上一段曲线是三次贝塞尔曲线:
S 的第一个控制点,是上个三次贝塞尔曲线 [第二控制点] 关于 [S 起点] 的对称点。
否则:
S 的第一个控制点,为 S 起点。
如下所示,理解 S
就是理解下面的 Sp1
点是什么。
---->[09_CS.svg]----
<path d="M10,40 C20,10 40,10 50,40 S90,70 90,20" stroke="#000082"/>
在数学上,如果 p0
点和 p1
点关于 p
点对称,那么它们的坐标应该满足下面的关系:
记: p0(x0,y0) p1(x1,y1) p(x,y)
若: p0 和 p1 点关于 p 对称
则坐标关系满足: (x0 x1)/2 = x (y0 y1)/2 = y
这样已知 p0 和 p 点坐标,就很容易求出 p1 的坐标:
x1 = 2*x - x0
y1 = 2*y - y0
另外,s
表示相对坐标,效果同理。
5.光滑形二次贝塞尔曲线 T/t
T/t
指令也类似:
若 T 的上一段曲线是二次贝塞尔曲线:
T 的控制点,是上个二次贝塞尔曲线 [控制点] 关于 [S 起点] 的对称点。
否则:
T 的控制点,为 T 起点。
下面是 T
上一段为 Q
的测试效果,思考一下 Tp
点是什么?
<path d="M10,40 Q30,60 50,40 T90,40" stroke="#000082" stroke-width="1"/>
三、本系列收获
在 iconfont 中可以下载 svg
类型的图标,
通过解析 svg
可以直接通过 Flutter
绘制的 api
进行绘制,如下所示:
另外,对于 svg
的路径解析,pub
上 已经
有了完善的包 path_drawing ,已及基于该包,实现的 svg
文件显示包 flutter_svg 。每次看到其他大佬写的开源库,我都感觉自己写的代码像过家家一样。研究、思考和学习,希望有朝一日,我也可以像他们那样,看到他们眼中的风采。
最后附加一下使用 flutter_svg 展示的这只流传千古的虎头 svg:【extra_02_svg/08】