Flutter绘制之 - 玩转路径测量
我正在参加「掘金·启航计划」。
前言
上一篇介绍了绘制中的路径联合的使用,今天继续介绍一个路径绘制中另一个实用方法,路径测量,通过它我们可以追踪路径上的的任意坐标点,获取路径片段、路径上某一点的相关信息,如路径的长度、位置和角度等。
获取路径测量类
路径测量的核心方法是Path
类里的computeMetrics()
方法,
//forceClosed是否设置path路径强制闭合。
PathMetrics computeMetrics({bool forceClosed = false})
通过这个方法,会返回一个PathMetrics
对象,点进去源码看一下,


发现PathMetrics
继承了Iterable
,是一个迭代器,范型为PathMetric
,通过命名我们可知PathMetric
才是真正的路径测量类。PathMetrics
只是存储路径测量类的集合。
- 题外话:这里说下迭代器每次迭代时,都会更新当前
iterator
迭代器的位置,也就是说当我们只需迭代循环一次时可以使用迭代器,当有重复迭代需求时,可以将迭代器转为List<E>
或者Set<E>
使用。
回到正题,继续看下PathMetric
这个类,

发现这个类里也很简单,一共向外暴露了3
个成员变量,2
个成员方法,如下:
成员变量:
lenght
:表示当前路径的总长度。
isClosed
:表示当前路径是否闭合。
contourIndex
:表示当前路径在迭代器中的位置。
成员方法:
1、getTangentForOffset
:获取当前路径某个具体位置的信息。
Tangent? getTangentForOffset(double distance) {}
传入路径当前的长度,返回Tangent
类,那就接着看下这个类吧,

也很简单,暴露了两个成员变量,一个get
方法,如下:
postion
:当前位置坐标。
vertor
:当前位置曲线上切线的矢量信息,按照坐标系,取值范围:x、y
正向为0 ~ 1,负向为0 ~ -1。
angle
:当前位置曲线切线和x
轴正向顺时针的夹角。
可以看出这个类是主要还是描述当前路径某个位置的信息的。
2、核心方法extractPath()
,获取当前子路径。
//startWithMoveTo表示是否执行该路径moveTo方法,false表示默认从原点开始,一般为true。
Path extractPath(double start, double end, {bool startWithMoveTo = true}) {}
传入开始、结束路径位置,就能获取到当前子路径上任意一个区域的路径片段,使用非常的简单。
小结:这里通过PathMetric
类我们可以获取当前路径片段,并可以获取路径上某一个位置的信息。
PathMetrics & PathMetric
有了上述知识,我们就可以知道对于path
而言,一个path
其实可能是由多个子path
共同组成的,例如当path
调用 moveTo(x,y);
或者 addXXX();
方法时,这时就会生成一个新的path
片段被加入到PathMetrics
里,这样说可能理解有点抽象,下面我们用示例演示一下,例如下方路径:
Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 5
..color = Colors.black;
Path path = Path();
path.lineTo(50, 0);
path.lineTo(50, 50);
path.moveTo(-50, 0);
path.lineTo(-50, 50);
path.lineTo(0, 50);
canvas.drawPath(path, paint);
这时候看到的效果如下:
如果这时我们在最后进行路径闭合,效果如下:
发现只闭合了moveTo
之后的路径,之前的路径并没有发生改变,其实在调用moveTo
之后,再次进行路径操作就会生成一个新路径,如果想要闭合之前的路径,只需要在moveTo
之前close
即可,当然也可以迭代子路径进行强制闭合绘制。
这里在moveTo
之前再次执行路径闭合,效果如下:
看到时正常的两个路径闭合,但是是用一个path
来表示。
应用:利用路径测量绘制奥运五环
上面是路径测量的所有理论方法,下面用一个小例子说明下路径测量的应用,绘制一个奥运五环,如下:
奥运五环环环相扣,如果我们用正常圆环顺序绘制,
// 奥运五环
// 半径
double r = 50;
Path pA = Path();
Path pB = Path();
Path pC = Path();
Path pD = Path();
Path pE = Path();
pA.addOval(Rect.fromCenter(center: const Offset(-100, -100), width: r * 2, height: r * 2));
pB.addOval(Rect.fromCenter(center: const Offset(10, -100), width: r * 2, height: r * 2));
pC.addOval(Rect.fromCenter(center: const Offset(120, -100), width: r * 2, height: r * 2));
pD.addOval(Rect.fromCenter(center: const Offset(-45, -50), width: r * 2, height: r * 2));
pE.addOval(Rect.fromCenter(center: const Offset(65, -50), width: r * 2, height: r * 2));
canvas.drawPath(pA, paint..color = Colors.blue);
canvas.drawPath(pB, paint..color = Colors.black);
canvas.drawPath(pC, paint..color = Colors.red);
canvas.drawPath(pD, paint..color = Colors.yellow);
canvas.drawPath(pE, paint..color = Colors.green);
就会是下面这样,达不到环环相扣的效果。

思路: 如果我们可以将圆环进行路径测量获取每个圆环的片段,先绘制蓝色底部部分路径片段,再绘制黄色底部部分路径片段,之后再将蓝色剩余片段补齐,接着再绘制黑色底部的路径片段,再将黄色剩余部分补齐...最后就会形成一个环环相扣的奥运五环。
步骤:
首先,在坐标系中,绘制圆环是从x
轴正向方向顺时针绘制的,为了避免路径的相交点位于圆环重叠部分,这里我将起始位置设置为顺时针45度的位置。也就是下面这个位置,也相当于路径总长度的1/4
处,因为这个位置在所有圆环都没有相交点。
接下来根据上面的思路我们先绘制蓝色和黄色,

接下来,先绘制黄色,再补齐蓝色,就形成了如下的2环相扣图:

但是在奥运五环中,黄色不仅和蓝色相扣,还和黑色环环相扣,那么这里就在绘制黄色剩余区域时就得先绘制黑色底部区域路径片段,如下:

之后再补齐黄色区域,再绘制黑色剩余区域。如下:

剩下2个圆环同理,最终效果如下:

一个环环相扣的奥运五环就完成了。
最后配合动画看下整体绘制路径的变化,
可以看到,五环的每个圆环都被分割成不同的路径片段按顺序绘制,最终达到了环环相扣的效果。
最后
路径测量的应用场景非常广泛,典型的在与动画配合使用时,可以对路径的绘制过程有一个动画过程的呈现,并且在复杂的路径中获取路径上某一坐标点信息时非常方便,总而言之,路径测量时路径操作中非常重要的功能,可以说它将整体路径的数据进行了存储,通过路径测量我们就可以在路径上获取我们想要的信息。
转载自:https://juejin.cn/post/7248906639704260669