Flutter学习:使用CustomPaint绘制路径

Flutter学习:认识CustomPaint组件和Paint对象
Flutter学习:使用CustomPaint绘制路径
Flutter学习:使用CustomPaint绘制图形
Flutter学习:使用CustomPaint绘制文字
Flutter学习:使用CustomPaint绘制图片

drawPath 绘制路径

drawPath需要传递2个参数:

  • Path path 路径对象
  • Paint paint 绘制对象
Path path = Path()..moveTo(100, 100);
path.lineTo(250, 250);
path.lineTo(350, 180);
path.lineTo(200, 500);
// 控制路径是否闭合,可不写
path.close();
canvas.drawPath(path, paint);

image

path.moveTo和path.lineTo

path.moveTopath.lineTo一样,都需要传递2个参数:

  • double x用来设置点的横坐标(x轴)
  • double y用来设置点的纵坐标(y轴)
path.moveTo(80, 200);
path.lineTo(320, 400);
canvas.drawPath(path, paint);

其中p0path.moveTo设置的坐标,p1path.lineTo设置的坐标

image

path.relativeMoveTo和path.relativeLineTo

path.relativeMoveTopath.relativeLineTofiType的使用方法和path.moveTopath.lineTo一样,只是起始点不再是左上角,而是前一个点的坐标

path.close

用来设置路径是否自己闭合:

path.close();

image

path.reset

清除之前所有的Path对象。并重置原点为左上角。

path.reset();

path.fillType

关于fillType的资料可以查看以下文章:

fillType有两个枚举值:

  • PathFillType.nonZero:内部由有符号边缘交叉的非零和定义。对于给定点,如果从该点到无穷远的线与绕该点顺时针方向的线交叉的次数与与绕该点逆时针方向的线交叉的次数不同,则认为该点位于路径的内侧。
  • PathFillType.evenOdd:内部由奇数个边缘交叉定义。对于给定点,如果从该点到无穷远的线穿过奇数条线,则认为该点位于路径的内侧
Path path = Path()..moveTo(size.width / 2, 200);
path.lineTo(size.width / 4, 500);
path.lineTo(size.width / 7 * 6, 320);
path.lineTo(size.width / 7, 320);
path.lineTo(size.width / 4 * 3, 500);
path.close();
// 默认值
path.fillType = PathFillType.nonZero;
path.fillType = PathFillType.evenOdd;
canvas.drawPath(path, paint);

image

path.addArc

通过路径绘制圆弧

addArc(Rect oval, double startAngle, double sweepAngle)需要传递3个参数:

  • oval绘制一个矩形
  • startAngle圆弧开始处
  • sweepAngle圆弧开始到结束的角度大小
// 只有path.addArc方法,path.moveTo方法将会无效
Path path = Path();
// path.moveTo(size.width / 2, 200); 无效
Rect oval = Rect.fromPoints(const Offset(80, 80), const Offset(300, 180));
path.addArc(oval, 0, 4);
canvas.drawPath(path, paint);
// 有path.moveTo、path.lineTo、path.addArc这3个方法
// path.lineTo在谁的后面就跟谁连接,默认path.moveTo为(0,0)
Path path = Path();
path.moveTo(size.width / 2, 200);
Rect oval = Rect.fromPoints(const Offset(80, 80), const Offset(300, 180));
path.addArc(oval, 0, 4);
path.lineTo(250, 400);
canvas.drawPath(path, paint);

image

path.addOval、path.addRect、path.addRRect用法和path.addArc一样

path.addPath

该方法将复制一遍已绘制的路径,并进行偏移

addPath(Path path, Offset offset, {Float64List? matrix4})可以传递3个参数,2个必要的,一个可选的:

  • path已绘制的路径对象
  • offsetpath对象进行的偏移量
  • matrix4将矩阵平移给定偏移量

没有使用matrix4属性:

Path path = Path()..moveTo(100, 100);
path.lineTo(210, 240);
path.lineTo(80, 380);
path.addPath(path, const Offset(120, 120));
canvas.drawPath(path, paint);

使用了matrix4属性:

Path path = Path()..moveTo(100, 100);
path.lineTo(210, 240);
path.lineTo(80, 380);
// 变形(倾斜)路径
path.addPath(path, const Offset(120, 120), matrix4: Matrix4.skew(.1, .1).storage);
canvas.drawPath(path, paint);

image

path.extendWithPath

效果和path.addPath一样,属性也一样,只是该方法会将这两条线段连接起来:

Path path = Path()..moveTo(100, 100);
path.lineTo(210, 240);
path.lineTo(80, 380);
path.extendWithPath(path, const Offset(120, 120), matrix4: Matrix4.skew(.2, .4).storage);
canvas.drawPath(path, paint);

image

path.addPolygon

添加一条新路径

addPolygon(List points, bool close)需要传递2个参数:

  • points一系列点的坐标
  • close是否首位相连
path.moveTo(size.width / 2, 200);
path.lineTo(200, 380);
path.lineTo(80, 460);
List<Offset> points = const [
  Offset(100, 40),
  Offset(350, 240),
  Offset(200, 500),
];
path.addPolygon(points, true);
canvas.drawPath(path, paint);

image

path.arcTo

绘制圆弧路径

arcTo( Rect rect, double startAngle, double sweepAngle, bool forceMoveTo, )需要传递4个参数:

  • rect绘制一个矩形用来确定圆弧的位置
  • startAngle圆弧开始的角度
  • sweepAngle圆弧开始到结束的角度大小
  • forceMoveTo圆弧路径为新路径还是与原路径相连
path.moveTo(size.width / 2, 200);
path.lineTo(80, 460);
Rect rect = Rect.fromPoints(const Offset(80, 340), const Offset(280, 420));
// forceMoveTo为true
path.arcTo(rect, 0, 5, true);
// forceMoveTo为false
path.arcTo(rect, 0, 5, false);
canvas.drawPath(path, paint);

image

path.arcToPoint

绘制一个两点之间线段距离的直径圆弧

arcToPoint(Offset arcEnd, {Radius radius = Radius.zero, double rotation = 0.0, bool largeArc = false, bool clockwise = true})可以传递5个参数,1个必要的,4个可选的:

  • arcEnd确定圆弧的结束点坐标
  • radius为0时将绘制一条直线
  • rotation感觉没啥用
  • largeArc是否是大圆弧
  • clockwise决定圆弧的绘制是从左边还是右边
path.moveTo(100, 200);
// p0
path.arcToPoint(
  const Offset(320, 500),
  radius: const Radius.circular(.5),
  rotation: 2,
  argeArc: true,
  clockwise: false,
);
// p1
path.arcToPoint(
  onst Offset(160, 250),
  adius: const Radius.circular(1),
  otation: 0,
  argeArc: true,
  lockwise: false,
);
// p2
path.arcToPoint(
  const Offset(240, 375),
  radius: const Radius.circular(0),
  rotation: 5,
  largeArc: false,
  clockwise: true,
);
canvas.drawPath(path, paint);

image

path.relativeArcToPoint

效果和path.arcToPoint一样,只是起始点为前一个点

path.conicTo

绘制圆锥路径

conicTo(double x1, double y1, double x2, double y2, double w)需要传递5个参数:

  • x1是第一个点x轴的坐标
  • y1是第一个点y轴的坐标
  • x2是第二个点x轴的坐标
  • y2是第二个点y轴的坐标
  • w是权重值。如果权重大于1,则曲线为双曲线;如果权重等于 1,则为抛物线;如果小于 1,则为椭圆
path.moveTo(size.width / 2, 200);
// 权重=0
path.conicTo(80, 280, 300, 380, 0);
// 权重=1
path.conicTo(80, 280, 300, 380, 1);
// 权重=2
path.conicTo(80, 280, 300, 380, 2);
canvas.drawPath(path, paint);

image

path.relativeConicTo

效果和path.conicTo一样,只是起始点为前一个点

path.quadraticBezierTo

使用控制点 (x1,y1) 添加一个从当前点弯曲到给定点 (x2,y2) 的二次贝塞尔曲线段

quadraticBezierTo( double x1, double y1, double x2, double y2)需要传递4个参数:

  • x1是第一个点x轴的坐标
  • y1是第一个点y轴的坐标
  • x2是第二个点x轴的坐标
  • y2是第二个点y轴的坐标
path.moveTo(20, 200);
path.quadraticBezierTo(200, 80, size.width - 20, size.width);
canvas.drawPath(path, paint);

其中p0代表起始点位置(path.moveTo(20, 200)),p1的坐标是(x1, y1)p2的坐标是(x2, y2)

image

path.relativeQuadraticBezierTo

效果和path.quadraticBezierTo一样,只是起始点为前一个点

path.cubicTo

使用控制点 (x1,y1) 和 (x2,y2) 添加从当前点弯曲到给定点 (x3,y3) 的三次贝塞尔曲线段

cubicTo( double x1, double y1, double x2, double y2, double x3, double y3)需要传递6个参数:

  • x1是第一个点x轴的坐标
  • y1是第一个点y轴的坐标
  • x2是第二个点x轴的坐标
  • y2是第二个点y轴的坐标
  • x3是第三个点x轴的坐标
  • y3是第三个点y轴的坐标
path.moveTo(100, 200);
path.cubicTo(120, 120, 240, 360, 310, 360);
canvas.drawPath(path, paint);

其中p1代表p0点控制线的右边坐标,p2代表p3点控制线的左边坐标

image

path.relativeCubicTo

效果和path.cubicTo一样,只是起始点为前一个点

path.transform

复制线段,并对其路径进行变形。transform(Float64List matrix4)需要传递一个Float64List对象:

绘制第一条线段:

Paint paint = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 8
  ..color = Colors.red;
Path path = Path();
path.moveTo(80, 100);
path.lineTo(140, 200);
path.lineTo(320, 280);
path.lineTo(100, 400);
canvas.drawPath(path, paint);

绘制第二条线段:

Paint paint1 = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 8
  ..color = Colors.blue;
Path path1 = path.transform(Matrix4.skew(.1, .4).storage);
canvas.drawPath(path1, paint1);

image

path.getBounds

获取路径的边界矩形

path.moveTo(50, 200);
path.lineTo(300, 280);
// path.getBounds()的结果为Rect.fromLTRB(50.0, 200.0, 300.0, 280.0)
print(path.getBounds());
canvas.drawPath(path, paint);

image

path.shift

功能和path.add一样,只不过不会自动绘制一份路径,会把路径复制一份然后赋值给其他路径。

  • Offset offset,复制一份路径后设置偏移量

第一条路径:

Paint paint = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 8
  ..color = Colors.red;
Path path = Path();
path.moveTo(size.width / 2, 200);
path.lineTo(80, 450);
path.lineTo(size.width - 80, 450);
path.close();
canvas.drawPath(path, paint);

复制第一条路径,并偏移:

Paint paint1 = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 8
  ..color = Colors.blue;
Path path1 = path.shift(const Offset(50, 50));
canvas.drawPath(path1, paint1);

image

path.contains

测试给定点是否在路径内。也就是说,如果路径与Canvas.clipPath一起使用,该点是否位于路径的可见部分。

  • Offset point,设置要检测的点的位置
Offset point1 = const Offset(200, 320);
print(path.contains(point1));

Offset point2 = const Offset(200, 520);
print(path.contains(point2));

如果该点在路径内,就返回 true,否则返回 false。

image

path.computeMetrics

path.computeMetrics可以获取路径的详细信息。

computeMetrics({bool forceClosed = false})有一个可选参数:

  • forceClosed,如果没有使用path.close();,该方法获取的结果isClose将会为false。设置该值为trueisClose将会为true

我们先绘制一条普通路径:

Paint paint = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 8
  ..color = Colors.red;
Path path = Path();
path.moveTo(size.width / 2, 200);
path.lineTo(80, 450);
path.lineTo(size.width - 80, 450);
path.close();

如果直接对以上绘制的路径使用path.computeMetrics,会返回一个PathMetrics对象:

PathMetrics pathMetrics = path.computeMetrics();

一般我们需要用到的是PathMetric对象,获取该对象一共有以下几种方法:

// 如果只有一个PathMetric对象推荐使用该方法
PathMetric pathMetric = pathMetrics.single;
// 如果有很多个PathMetric对象使用以下任意方法
PathMetric pathMetric = pathMetrics.elementAt(0);
PathMetric pathMetric = pathMetrics.first;
PathMetric pathMetric = pathMetrics.last;

获取到PathMetric对象后,我们就可以沿着这条路径绘制一条新的路径,使用extractPath方法。

该方法可以传递3个参数:

  • double start:给定线段开始的距离
  • double end:给定线段结束的距离
  • bool startWithMoveTo:是否以 moveTo 点开始绘制线段
Path path1 = pathMetric.extractPath(50, 400, startWithMoveTo: true);
Path path1 = pathMetric.extractPath(50, 400, startWithMoveTo: false);

image

关于computeMetrics的方法和属性还有很多没讲到,等以后有时间了再慢慢研究。

posted @ 2022-03-11 17:25  菠萝橙子丶  阅读(1390)  评论(0编辑  收藏  举报