【Flutter 绘制番外】svg 终篇 - 路径指令

2022-04-15 17:16:43 浏览数 (1)

前情回顾

上两篇我们通过对 svg 路径 M/H/V/L/C/Q/Z 几个指令的解析。把 掘金 logosvg ,转化为 Flutter 的原生路径绘制,并且附加了一些绘制效果。

除了这些外,还有一些其他的指令。本篇的目的就是全面梳理一下 svgpath 标签下的路径命令。如下是一览表:

代码语言:javascript复制
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 点。

代码语言:javascript复制
---->[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)

代码语言:javascript复制
---->[02_m_v.svg]----    
<path d="M30,20 H80 V40Z m30,30 v30" stroke="#000082" />
3.绝对坐标和相对坐标使用场景

如果能够精确的知道某点的坐标,使用 绝对坐标 自然是最方便的。但很多时候,绝对坐标并能直接获取,比如下面 A 点右移 50,下移 20,到 B 点。 虽然可以根据数据计算出 B 的绝对坐标,但比较麻烦,特别是对于一些曲线路径,相对偏移会非常方便。

代码语言:javascript复制
---->[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 ,它与起点和终点的连线和曲线相切,如虚线所示:

代码语言:javascript复制
---->[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与终点 连线和曲线相切,如虚线所示:

代码语言:javascript复制
---->[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 形成。弧线本质上是从 椭圆上截取弧线 ,前两个值是椭圆的两个半轴长度;第四个值表示是否取大圆弧,如下实线部位取大圆弧,虚线部位取小圆弧;第五个值代表是否顺时针,如下实线部顺时针,虚线部位逆时针;第六第七值代表结束点坐标。

代码语言:javascript复制
---->[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 轴的倾斜角度,同时需要满足椭圆过起止点。

代码语言:javascript复制
---->[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 和起点重合,控制点 240,70。如下的 SC 是同一条曲线:

代码语言:javascript复制
---->[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 最难把握的一点是:

代码语言:javascript复制
若 S 的上一段曲线是三次贝塞尔曲线: 
S 的第一个控制点,是上个三次贝塞尔曲线 [第二控制点] 关于 [S 起点] 的对称点。
否则:
S 的第一个控制点,为 S 起点。

如下所示,理解 S 就是理解下面的 Sp1 点是什么。

代码语言:javascript复制
---->[09_CS.svg]----    
<path d="M10,40 C20,10 40,10 50,40 S90,70 90,20" stroke="#000082"/>

在数学上,如果 p0 点和 p1 点关于 p 点对称,那么它们的坐标应该满足下面的关系:

代码语言:javascript复制
记: 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 指令也类似:

代码语言:javascript复制
若 T 的上一段曲线是二次贝塞尔曲线: 
T 的控制点,是上个二次贝塞尔曲线 [控制点] 关于 [S 起点] 的对称点。
否则:
T 的控制点,为 T 起点。

下面是 T 上一段为 Q 的测试效果,思考一下 Tp 点是什么?

代码语言:javascript复制
<path d="M10,40 Q30,60 50,40 T90,40" stroke="#000082" stroke-width="1"/>
三、本系列收获

在 iconfont 中可以下载 svg 类型的图标,

通过解析 svg 可以直接通过 Flutter 绘制的 api 进行绘制,如下所示:

通这三篇文章,实现了一个及其简陋的 svg 解析器。虽然没有什么实际的应用价值,但是我们认识了 svgpath 各指令的含义。这是更为基础的知识积累,通过 svg 路径与Flutter 绘制的联系,也可以锻炼 Flutter 的绘制技能。另外尝试对 svg 的解析,其中发现问题和解决问题的过程,更是个人经验的累积。对于正则表达式的使用,也得到了一定的训练。

另外,对于 svg 的路径解析,pub已经 有了完善的包 path_drawing ,已及基于该包,实现的 svg 文件显示包 flutter_svg 。每次看到其他大佬写的开源库,我都感觉自己写的代码像过家家一样。研究、思考和学习,希望有朝一日,我也可以像他们那样,看到他们眼中的风采。

最后附加一下使用 flutter_svg 展示的这只流传千古的虎头 svg:【extra_02_svg/08】

0 人点赞