对一些有趣的绘制 技能
和知识
, 我会通过 [番外篇]
的形式加入《Flutter 绘制指南 - 妙笔生花》小册中,一方面保证小册的“与时俱进”
和 “活力”
。另一方面,是为了让一些重要的知识有个 好的归宿
。本文源码可以看这里。
另外一个好消息: 《Flutter 绘制指南 - 妙笔生花》小册源码 idraw 已经完成了 空安全
的转化。
一、对 svg 的认识
1. 初见
通过 F12
可以看到掘金的 logo
是一个 svg
,可以将它作为文件下载。
打开后可以看出其中有很多不明所以的字符,可以确定的是:这些字符决定了 logo
的显示。至于这些字符为什么可以控制显示,又如何控制显示,对于初见者并不能理解。
2. 试探
在 AdroidStudio
中可以实时显示 svg
文件的表现效果,如下将一段 path
注释掉,可以看出 稀
少了一块。
再注释掉一个 path
,可以看到又少了一块。所以可以看出,每个 path
块都表示一部分的路径。
二、直线路径操作符
1. 水平和竖直 绝对
路径: H
、V
如下是 M0,0 H50 V50 H0
的效果:
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0,0 H50 V50 H0" fill="#FFFFFF"/>
</svg>
M0,0
表示:从起点移至 0,0
。
H50
表示:水平移动 到
横坐标 50
处。
V50
表示:竖直移动 到
纵坐标 50
处。
H0
表示:水平移动 到
横坐标 0
处。
最后路径会连到起点 0,0
,另外 fill="#FFFFFF"
表示填充白色。
2.绝对移动 : M
如下橙色区域是 M20,0 H50 V50 H0
的效果,M20,0
表示路径起点移至 (20,0)
坐标。
4.直线绝对
路径:L
如下蓝色区域是 M28,0 L43,0 12,32 4,25
的效果,只要知道点的坐标就可以通过 L
符号,将点依次拼接形成路径。
只要知道图形所有的点位坐标,就可以形成路径。将多段路径合在一起,就可以来显示期望的图案,比如下面的 Flutter
图标。
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0,0 H50 V50 H0" fill="#FFFFFF" />
<path d="M28,2 L43,2 12,32 4,25" fill="#3AD0FF" />
<path d="M16,36 L30,48 44,48 23,29" fill="#00559E" />
<path d="M16,36 L28,24 42,24 24,43" fill="#3AD0FF" />
</svg>
仔细看一下,就可以发现,这么复杂的字符,也只是 M/H/V/L
四个操作符的拼接而已。另外注意一下,逗号 ,
和空格 可以互换。一般的 svg
文件都是由 设计软件
生成的,所以都是空格,一般不具有可读性。
其实对于 Flutter 绘制而言,最重要的是路径 Path
的形成,那么既然 svg
文件里有路径信息,是不是意味着我们可以提取坐标、生成路径,然后进行绘制呢?废话不多说,一起来试验一下。
三、svg 直线型操作符转化为 Path 对象
1. 如何对 svg 路径进行解析
现在的问题在于如何将 svg
路径解析处我们需要的信息,对一字符串的处理,自然是非 正则
莫属了。只要写出一条何时的正则,进行匹配即可。
<path d="M17.5865 17.3955H17.5902L28.5163 8.77432L25.5528 6.39453L17.5902 12.6808H17.5865L17.5828 12.6845L9.62018 6.40201L6.6604 8.78181L17.5828 17.3992L17.5865 17.3955Z" fill="#1E80FF"/>
在后期会发布一个关于 正则的小册
,其中会通过实现如下的正则校验的应用,来让大家更有趣地认识正则。目前文件中只有 M,H,V,L
四个指令,通过下面的命令就可以匹配每段指令的信息。
通过匹配后,我们就可以获取其中必要的信息,如下所示:
2. 与 Flutter 绘制的衔接
如下方法是通过解析一条 svg
路径,形成 Flutter
中 Path
的过程。注意目前只有 M,H,V,L,Z
四个指令,其他 svg
指令在后面会继续完善。通过绘制形成的路径就能显示出来了:
Color color = const Color(0xff1E80FF);
canvas.drawPath(formPathFromSvgOp(src), paint..color=color);
代码语言:javascript复制Path formPathFromSvgOp(String src) {
Path path = Path();
RegExp regExp = RegExp(r'[M,H,V,L](((d .d )|d)([ ,])?) |Z');
List<RegExpMatch> results = regExp.allMatches(src).toList();
double lastX = 0;
double lastY = 0;
results.forEach((RegExpMatch element) {
String? op = element.group(0);
if (op != null) {
if (op.startsWith("M")) {
List<String> pos = op.substring(1).split(RegExp(r'[, ]'));
double dx = num.parse(pos[0]).toDouble();
double dy = num.parse(pos[1]).toDouble();
path.moveTo(dx, dy);
lastX = dx;
lastY = dy;
}
if (op.startsWith("L")) {
List<String> pos = op.substring(1).split(RegExp(r'[, ]'));
for (int i = 0; i < pos.length; i = 2) {
double dx = num.parse(pos[i]).toDouble();
double dy = num.parse(pos[i 1]).toDouble();
path.lineTo(dx, dy);
lastX = dx;
lastY = dy;
}
}
if (op.startsWith("H")) {
print(op.length);
List<String> pos = op.substring(1).trim().split(RegExp(r'[, ]'));
for (int i = 0; i < pos.length; i ) {
print('${pos[i]}');
double dx = num.parse(pos[i]).toDouble();
double dy = lastY;
path.lineTo(dx, dy);
lastX = dx;
}
}
if (op.startsWith("V")) {
List<String> pos = op.substring(1).trim().split(RegExp(r'[, ]'));
for (int i = 0; i < pos.length; i ) {
double dx = lastX;
double dy = num.parse(pos[i]).toDouble();
path.lineTo(dx, dy);
lastY = dy;
}
}
if (op.startsWith("Z")) {
path.close();
}
}
});
return path;
}
这样只要把三个路径画出来,就可以形成掘金的 logo
,如下所示:
这样 svg
文件通过 path
进行分割,遍历形成路径即可。注意一下,效果上来看,文字部分似乎不是很好,因为这里 只解析了 M、H、L、V
四个指令。仔细来看,这个文件中还有 C
指令用于形成贝塞尔曲线,这个指令在下一篇进行讲解,使用目前效果不好是正常的。下一章对 C
解析后就可以完美了。
3.颜色的解析
同样通过正则表达式匹配每条路径中的颜色信息,如下所示:
由于每条 svg
路径都有路径信息和颜色信息,使用可以定义一个 SVGPathResult
对象进行维护。
class SVGPathResult{
final Color color;
final Path path;
SVGPathResult({required this.color, required this.path});
}
对颜色的解析逻辑如下:
代码语言:javascript复制 SVGPathResult formPathFromSvgOp(String src) {
Color resultColor = Colors.black;
RegExp color = RegExp(r'(?<=#)(.*)(?=")');
List<RegExpMatch> colorResult = color.allMatches(src).toList();
if(colorResult.length>0){
String? colorStr = colorResult[0].group(0);
if(colorStr!=null){
print(colorStr);
resultColor = Color(int.parse(colorStr, radix: 16) 0xFF000000);
}
}
这样,之前写的 Flutter
图标的 svg
就可以解析涂色的,效果如下:
本文主要介绍了 H、V、L
三个绝对直线路径的使用以及正则解析,用于 Flutter
中 Path
对象的形成。svg
中还有一些其他比较重要的操作符,我们将在接下来的文章中介绍,并对其进行解析。所以 千万别以为这点解析逻辑能解析任何 svg 文件
,后续还需很多细节的完善。那本文就到这里,谢谢观看~