flutter 路径的用法

[1]. 了解如何通过移动路径形成形状:直线移动、圆弧移动、圆锥曲线移动、贝塞尔曲线移动。
[2]. 了解路径的 [绝对移动] 和 [相对移动]。
[3]. 了解在已有的路径中添加其他形状:添加矩形、圆角矩形、椭圆、圆弧、多边形、其他路径。
[4]. 使用 path 绘制坐标系。

一、路径加入方法

下图是路径形成的基础方法,包括路径的移动、加入直线圆弧圆锥曲线贝塞尔曲线
对这些 API 的掌握程度,直接决定你运用路径的能力。


1.moveTolineTo: 画线

下面画布已经移动到屏幕中心,并且 y轴向正下方。想象一下,你现在手里拿着一只笔。
moveTo相当于提起笔落到纸上的位置坐标,且坐标以画布原点为参考系
lineTo相当于从落笔点画直线到期望的坐标点,且坐标以画布原点为参考系

---->[p05_path/01_moveTo_lineTo/paper.dart]----
Path path = Path();
Paint paint = Paint()
  ..color = Colors.deepPurpleAccent
  ..style = PaintingStyle.fill;
path
  ..moveTo(0, 0) //移至(0,0)点
  ..lineTo(60, 80) //从(0,0)画线到(60, 80) 点
  ..lineTo(60, 0) //从(60,80)画线到(60, 0) 点
  ..lineTo(0, -80) //从(60, 0) 画线到(0, -80)点
  ..close(); //闭合路径
canvas.drawPath(path, paint);

paint
  ..style = PaintingStyle.stroke
  ..strokeWidth = 2;
path
  ..moveTo(0, 0)
  ..lineTo(-60, 80)
  ..lineTo(-60, 0)
  ..lineTo(0, -80);
canvas.drawPath(path, paint);

2.relativeMoveTorelativeLineTo: 相对画线

如果点位已经知道,使用 moveTo 和 lineTo 会比较方便,但很多情况下是不能直接知道的。
比如在某点的基础上,画一条线,要求左移 10,上移 60,这样点位很难直接确定。
使用 relative 系列方法就会非常简单。如下图形的路径绘制,不用相对坐标会很复杂。
使用相对的坐标会更方便调整(左侧只需移动起始点即可全部移动)

---->[p05_path/02_relativeMoveTo_relativeLineTo/paper.dart]----
Path path = Path();
Paint paint = Paint()
  ..color = Colors.green
  ..style = PaintingStyle.fill;
path
  ..relativeMoveTo(0, 0)
  ..relativeLineTo(100, 120)
  ..relativeLineTo(-10, -60)
  ..relativeLineTo( 60,-10,)
  ..close();
canvas.drawPath(path, paint);

path.reset();
paint
  ..style = PaintingStyle.stroke..color=Colors.green
  ..strokeWidth = 2;
path
  ..relativeMoveTo(-200, 0)
  ..relativeLineTo(100, 120)
  ..relativeLineTo(-10, -60)
  ..relativeLineTo( 60,-10,)..close();
canvas.drawPath(path, paint);

3.arcTo: 圆弧

arcTo 用于圆弧路径,指定一个矩形域,形成椭圆
指定起始弧度,和扫描弧度,就可以从椭圆上截取出圆弧
最后一参代表是否强行移动,如果为true,如图左,绘制圆弧时会先移动到起点

---->[p05_path/03_arcTo/paper.dart]----
Path path = Path();
Paint paint = Paint()
  ..color = Colors.purpleAccent
  ..strokeWidth = 2
  ..style = PaintingStyle.stroke;

// 绘制左侧
var rect = Rect.fromCenter(center: Offset(0, 0), width: 160, height: 100);
path.lineTo(30, 30);
path..arcTo(rect, 0, pi * 1.5, true);
canvas.drawPath(path, paint);

path.reset();
canvas.translate(200, 0);
// 绘制右侧
path.lineTo(30, 30);
path..arcTo(rect, 0, pi * 1.5, false);
canvas.drawPath(path, paint);

4.arcToPointrelativeArcToPoint: 点定弧

当想要画圆弧到某个点,用 arcToPoint 会非常方便
接受一个点位入参 Offset,是圆弧的终点,可指定圆弧半径radius、是否使用优弧、是否顺时针
左侧: 使用优弧: largeArc: true ,逆时针:clockwise: false
中间: 使用劣弧: largeArc: false ,顺时针:clockwise: true
右侧: 使用优弧: largeArc: true ,顺时针:clockwise: true

---->[p05_path/04_arcToPoint_relativeArcToPoint/paper.dart]----
Path path = Path();
Paint paint = Paint()
  ..color = Colors.purpleAccent
  ..strokeWidth = 2
  ..style = PaintingStyle.stroke;
path.lineTo(80, -40);

//绘制中间
path..arcToPoint(
  Offset(40, 40),
  radius: Radius.circular(60),
  largeArc: false,
)..close();
canvas.drawPath(path, paint);

path.reset();
canvas.translate(-150, 0);
//绘制左侧
path.lineTo(80, -40);
path..arcToPoint(
    Offset(40, 40),
    radius: Radius.circular(60),
    largeArc: true,
    clockwise: false
)..close();
canvas.drawPath(path, paint);

path.reset();
canvas.translate(150+150.0, 0);
//绘制右侧
path.lineTo(80, -40);
path..arcToPoint(
  Offset(40, 40),
  radius: Radius.circular(60),
  largeArc: true,
)..close();
canvas.drawPath(path, paint);

relativeArcToPoint方法即使用相对位置来加入圆弧路径,参数含义与上面一致。


5.conicTorelativeConicTo: 圆锥曲线

conicTo 接收五个参数用于绘制圆锥曲线,包括椭圆线抛物线双曲线
其中前两参是控制点,三四参是结束点,第五参是权重。(下图已画出辅助点)
权重< 1 时,圆锥曲线是椭圆线,如下左图
权重= 1 时,圆锥曲线是抛物线,如下中图
权重> 1 时,圆锥曲线是双曲线,如下右图

---->[p05_path/05_conicTo_relativeConicTo/paper.dart]----
final Offset p1 = Offset(80, -100);
final Offset p2 = Offset(160, 0);

Path path = Path();
Paint paint = Paint()
  ..color = Colors.purpleAccent
  ..strokeWidth = 2
  ..style = PaintingStyle.stroke;

//抛物线
path.conicTo(p1.dx, p1.dy, p2.dx, p2.dy, 1);
canvas.drawPath(path, paint);

path.reset();
canvas.translate(-180, 0);
//椭圆线
path.conicTo(p1.dx, p1.dy, p2.dx, p2.dy, 0.5);
canvas.drawPath(path, paint);

path.reset();
canvas.translate(180+180.0, 0);
//双曲线
path.conicTo(p1.dx, p1.dy, p2.dx, p2.dy, 1.5);
canvas.drawPath(path, paint);

relativeConicTo方法即使用相对位置来加入圆锥曲线路径,参数含义与上面一致。


6.quadraticBezierTorelativeQuadraticBezierTo: 二阶贝塞尔

quadraticBezierTo接收四个参数用于绘制二阶贝塞尔曲线。
其中前两参是控制点,三四参是结束点。(下图已画出蓝色辅助点线)
relativeQuadraticBezierTo是在使用相对位置来加入二阶贝塞尔曲线路径。
注: 贝塞尔曲线,在后面章节会有专题讲解,此处只是简单介绍。

---->[p05_path/06_quadraticBezier/paper.dart]----
final Offset p1 = Offset(100, -100);
final Offset p2 = Offset(160, 50);

Path path = Path();
Paint paint = Paint()
  ..color = Colors.purpleAccent
  ..strokeWidth = 2
  ..style = PaintingStyle.stroke;
path.quadraticBezierTo(p1.dx, p1.dy, p2.dx, p2.dy);
path.relativeQuadraticBezierTo(p1.dx, p1.dy, p2.dx, p2.dy);
canvas.drawPath(path, paint);

7.cubicTorelativeCubicTo: 三阶贝塞尔

quadraticBezierTo接收六个参数用于绘制三阶贝塞尔曲线
其中前两参是控制点1,三四参是控制点2,五六参是结束点。(下图已画出蓝色辅助点线)
relativeCubicTo是在使用相对位置来加入三阶贝塞尔曲线路径。

图片
---->[p05_path/07_cubicTo/paper.dart]----
Path path = Path();
Paint paint = Paint();
paint
  ..color = Colors.purpleAccent
  ..strokeWidth = 2
  ..style = PaintingStyle.stroke;
path.cubicTo(p1.dx, p1.dy, p2.dx, p2.dy, p3.dx, p3.dy);

path.relativeCubicTo(p1.dx, p1.dy, p2.dx, p2.dy, p3.dx, p3.dy);
canvas.drawPath(path, paint);

二、为路径添加已有形状

可以在已知路径上添加矩形圆角矩形椭圆圆弧多边形路径


1. addRectaddRRect: 添加类矩形

addRect用于在已有路径上添加矩形路径,接受一个Rect对象
addRRect用于在已有路径上添加圆角矩形路径,接受一个RRect对象

---->[p05_path/08_addRect_addRRect/paper.dart]----
Path path = Path();
Paint paint = Paint()
  ..color = Colors.purpleAccent
  ..strokeWidth = 2
  ..style = PaintingStyle.stroke;

Rect rect = Rect.fromPoints(Offset(100, 100), Offset(160, 160));
path
  ..lineTo(100, 100)
  ..addRect(rect)
  ..relativeLineTo(100, -100)
  ..addRRect(RRect.fromRectXY(rect.translate(100, -100), 10, 10));
canvas.drawPath(path, paint);

2. addOvaladdArc: 添加类圆形

addOval用于在已有路径上添加椭圆路径,接受一个Rect 对象
addArc用于在已有路径上添加圆弧路径,接受一个Rect 对象,起始弧度、扫描弧度

---->[p05_path/09_addOval_addArc/paper.dart]----
Path path = Path();
Paint paint = Paint()
  ..color = Colors.purpleAccent
  ..strokeWidth = 2
  ..style = PaintingStyle.stroke;

Rect rect = Rect.fromPoints(Offset(100, 100), Offset(160, 140));
path
  ..lineTo(100, 100)
  ..addOval(rect)
  ..relativeLineTo(100, -100)
  ..addArc(rect.translate(100 + 60.0, -100), 0, pi);
canvas.drawPath(path, paint);

3. addPolygon: 添加多边形路径 、addPath:添加路径

addPolygon用于在已有路径上添加多边形路径,接受一个List<Offset>对象
addPath用于在已有路径上添加路径,接受一个Path对象和偏移量Offset

图片
---->[p04_path/10_addPolygon/paper.dart]----
Path path = Path();
Paint paint = Paint()
  ..color = Colors.purpleAccent
  ..strokeWidth = 2
  ..style = PaintingStyle.stroke;
var p0 = Offset(100, 100);
path
  ..lineTo(100, 100)
  ..addPolygon([
    p0,
    p0.translate(20, -20),
    p0.translate(40, -20),
    p0.translate(60, 0),
    p0.translate(60, 20),
    p0.translate(40, 40),
    p0.translate(20, 40),
    p0.translate(0, 20),
  ], true)
  ..addPath(
      Path()..relativeQuadraticBezierTo(125, -100, 260, 0), Offset.zero)
  ..lineTo(160, 100);
canvas.drawPath(path, paint);

三、使用 Path 实现网格坐标系

前面已经用过 Canvas 绘线的方式实现了网格坐标系,那为什么还要用 Path 再做一遍呢?
用 Canvas 绘线要画很多次,还伴随 Canvas 的移动。而 Path 则是收集路径,一次画完。
这样无论从性能方面还是代码简洁性方面都比之前好。绘线路径如下:

---->[p05_path/11_path_coo/coordinate.dart]----
void _drawGridLine(Canvas canvas, Size size) {
  _gridPaint
    ..style = PaintingStyle.stroke
    ..strokeWidth = .5
    ..color = Colors.grey;
  
  for (int i = 0; i < size.width / 2 / step; i++) {
    _gridPath.moveTo(step * i, -size.height / 2 );
    _gridPath.relativeLineTo(0, size.height);
    _gridPath.moveTo(-step * i, -size.height / 2 );
    _gridPath.relativeLineTo(0, size.height);
  }
  
  for (int i = 0; i < size.height / 2 / step; i++) {
    _gridPath.moveTo(-size.width / 2,step * i );
    _gridPath.relativeLineTo(size.width,0 );
    _gridPath.moveTo(-size.width / 2,-step * i,  );
    _gridPath.relativeLineTo(size.width,0 );
  }
  canvas.drawPath(_gridPath, _gridPaint);
}

另外,我将绘制坐标系的逻辑封装到了Coordinate类中,方便以后的使用。比如今后想要绘制一个坐标系,只需要两步,在之后的示例中将使用这个坐标系。

class PaperPainter extends CustomPainter {
  final Coordinate coordinate = Coordinate(step: 25); // 定义坐标系
  
  @override
  void paint(Canvas canvas, Size size) {
    coordinate.paint(canvas, size); // 绘制坐标系
  }

  @override
  bool shouldRepaint(PaperPainter oldDelegate) => false;
}

到这里,基本的路径添加操作就介绍完了,下一节将介绍路径的操作方法。

THE END
喜欢就支持一下吧
点赞14 分享