likes
comments
collection
share

看完这篇,你也可以搞定有趣的动态曲线绘制

作者站长头像
站长
· 阅读数 10

前言

前面我们用了几篇文章了解 Paint 类的使用,有兴趣的了解的可以看一下下面的文章:

接下来我们来了解一下 Path 类的一些特性。Path 类用于描述绘制路径,可以实现绘制线段、曲线、自定义形状等功能。本篇我们介绍 Path 的一个描述类 PathMetric 的应用。通过本篇你会了解以下两方面的内容:

  • PathMetric 类简介。
  • PathMetric 的应用。

PathMetric 简介

PathMetric 是一个用于测量 Path 和抽取子路径(sub-paths) 的工具,通过 Path 类的 computeMetrics方法可以返回一组PathMetric 类。为什么是一组,而不是一个呢?这是因为 Path 可能包含多个不连续的子路径,比如通过 moveTo 可以重新开启新的一段路径。 通过 PathMetric 可以获取到 Path 的长度,路径是否闭合,以及某一段路径是否是 Path的子路径。PathMetrics 是一个迭代器,因此在不获取其中的 PathMetric 对象时,并不会实际进行 Path 的相关计算,这样可以提高效率。另外需要注意的是,通过 computeMetrics 方法计算得到的是一个当前Path 对象的快照,如果在之后更改了 Path 对象,并不会进行更新。 我们来看一下 PathMetric 的一些属性和方法。

  • lengthPath 对象其中一段(独立的)的长度;
  • isClosed:判断 Path 对象是否闭合;
  • contourIndex:当前对象在 PathMetrics 中的次序;
  • getTangentForOffset:这个方法通过距离起点的长度的偏移量(即从0 到 length 中的某个位置)返回一个 Tangent 对象,通过这个对象可以获取到 Path 某一段路径途中的任意一点的位置以及角度。以下面的图形为例,从点(0, 0)到点(2, 2)的线段总长度为2.82,如果我们通过getTangentForOffset获取距离起始点1.41 的位置的Tangent 对象,就会得到该位置的坐标是(1, 1),角度是45度(实际以弧度的方式计算)。

看完这篇,你也可以搞定有趣的动态曲线绘制

  • extractPath:通过距离 Path 起点的开始距离和结束距离获取这段路劲的子路径,如下图所示。

看完这篇,你也可以搞定有趣的动态曲线绘制

PathMetric 应用

我们来通过 PathMetric 实现下面动图的效果。 看完这篇,你也可以搞定有趣的动态曲线绘制 这张图最开始绘制的是一条贝塞尔曲线,是通过 Path 自带的贝塞尔曲线绘制的,代码如下所示。

Path path = Path();
final curveHeight = 60.0;
final stepWidth = size.width / 4;
path.moveTo(0, size.height / 2);
path.quadraticBezierTo(size.width / 2 - stepWidth,
    size.height / 2 - curveHeight, size.width / 2, size.height / 2);
path.quadraticBezierTo(size.width / 2 + stepWidth,
    size.height / 2 + curveHeight, size.width, size.height / 2);

quadraticBezierTo这个方法就是从 Path 当前的终点到参数3,4(参数名为 x2,y2)绘制一条贝塞尔曲线,控制点为参数1,2(参数名为 x1,y1)。 动画过程中曲线上的红色圆点就是通过 PathMetric 得到的,动画对象 Animation 的值从0-1变化,我们通过这个值乘以曲线的长度就能得到getTangentForOffset方法所需的偏移量,然后就可以确定动画过程中绘制圆点的位置了,代码如下所示。

for (var pathMetric in metrics) {
    var tangent =
        pathMetric.getTangentForOffset(pathMetric.length * animationValue);
  
    paint.style = PaintingStyle.fill;
    canvas.drawCircle(tangent!.position, 4.0, paint);
}

接下来是动画过程中的我们看到红色曲线会逐步覆盖蓝色曲线,这就是用 extractPath获取子路径完成的,在动画过程,我们控制 extractPath的结束位置,就可以逐步完成原有曲线的覆盖了,实现代码只有两行,如下所示。

var subPath =
    pathMetric.extractPath(0.0, pathMetric.length * animationValue);
canvas.drawPath(subPath, paint);

最后是底下的填充,填充我们使用了渐变色,这个利用了之前我们讲过的Paint 对象的 shader 属性实现,具体可以参考之前的文章。填充其实就是一段闭合的 Path,只是在动画过程中控制右边绘制的边界就可以了,然后上面跟随曲线的部分还是基于子路径完成的。填充部分实现代码如下。

var fillPath = Path();
fillPath.moveTo(0, size.height);
fillPath.lineTo(0, size.height / 2);
fillPath.addPath(subPath, Offset(0, 0));
fillPath.lineTo(tangent.position.dx, size.height);
fillPath.lineTo(0, size.height);
paint.shader = LinearGradient(
  begin: Alignment.topCenter,
  end: Alignment.bottomCenter,
  colors: [Colors.red[400]!, Colors.blue[50]!],
).createShader(Rect.fromLTRB(
  0,
  size.height / 2 - curveHeight,
  size.width,
  size.height,
));
canvas.drawPath(fillPath, paint);

完整代码已经提交至:绘图相关代码,文件名为:path_metrics_demo.dart

总结

本篇介绍了 Flutter 路径Path 的工具类 PathMetric的介绍和应用,通过 PathMetric 我们可以定位到 Path 的指定位长度的位置的信息,也可以通过起始点从 Path 中抽取子路径。有了这些基础,就可以实现很多场景的应用,比如曲线上布局标识或填充,标记指定位置的点等等。

我是岛上码农,微信公众号同名,这是Flutter 入门与实战的专栏文章,提供体系化的 Flutter 学习文章。对应源码请看这里:Flutter 入门与实战专栏源码。如有问题可以加本人微信交流,微信号:island-coder

👍🏻:觉得有收获请点个赞鼓励一下!

🌟:收藏文章,方便回看哦!

💬:评论交流,互相进步!

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿