直观理解
Path就是你要画的形状的线条路径,可以预想用笔画的过程:
- 在纸上选择落笔点
- 从落笔点向其他点画
- 画直线
- 画曲线
- 画个形状
- 一个形状画完后,可以抬笔移到另一个点,然后继续画。
- 当线条画到最后,也就是最后一个点,可以看下最后一个点和当前线条第一个点是否一致,可以一致也可以不一致
Path就是你画的过程的路径记录。
方法汇总
选择落笔点:
调用move后,注意是抬笔后直接去两一个点,前后不连接:
- moveTo(double x, double y),开启一个线条的起点,参数是绝对位置
- relativeMoveTo(double dx, double dy),相对当前点位置,在(dx,dy)处重新下笔
画直线:
- lineTo(double x, double y),画直线,到(x,y)
- relativeLineTo(double dx, double dy),画直线,相对当前点位置(x,y),画到当前点位置的(dx,dy)处,按绝对位置,就是(x+dx,y+dy)
void _testMove(Canvas canvas, Size size) {
Paint paint = new Paint();
paint
..color = Colors.redAccent[200]
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = 4;
Path path = Path();
path.moveTo(-0,0);
path.lineTo(80, 80);
path.relativeLineTo(0, -80);
path.close();
path.relativeMoveTo(-80,-160);
path.lineTo(80, -80);
path.relativeLineTo(0, -80);
canvas.drawPath(path, paint);
}
画弧线
- void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo),当前path,连接一个独立的弧,用矩形描述弧线
- forceMoveTo,当前path末端与新的弧的连接方式,false,表示二者相连,true表示直接move过去,不相连。通过参数确定的弧是一个独立的线,有自己的起始点,path的末端与弧的起点不一定一直,所以才有这个字段,用于确定二者是否相连。
void _drawArtto(Canvas canvas, Size size) {
Paint paint = new Paint();
paint
..color = Colors.redAccent[200]
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = 4;
Rect rect = Rect.fromCenter(center: Offset(0, 0), width: 300, height: 200);
Path path = Path();
path.moveTo(0, 0);
path.lineTo(40, 40);
path.arcTo(rect,0, pi * 1.2, false);
canvas.drawPath(path, paint);
canvas.translate(0, -200);
path.reset();
path.moveTo(0, 0);
path.lineTo(40, 40);
path.arcTo(rect,0, pi * 1.2, true);
canvas.drawPath(path, paint);
}
- arcToPoint(Offset arcEnd, { Radius radius = Radius.zero, double rotation = 0.0, bool largeArc = false, bool clockwise = true, }) 指定一个绝对位置点,从当前path的末端画一个弧到该点 - largeArc,是否使用优弧, true 优弧,false 劣弧 - clockwise,是否顺时针画 - 这两个参数一块儿理解,顺时针画劣弧,顺时针画优弧,逆时针画劣弧,逆时针画优弧
void _drawArttoPoint(Canvas canvas, Size size) {
Paint paint = new Paint();
paint
..color = Colors.redAccent[200]
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = 2;
canvas.translate(0, -300);
Path path = Path();
path.moveTo(0, 0);
path.lineTo(80, -40);
path.arcToPoint(Offset(80, 40),
radius: Radius.circular(60), largeArc: false, clockwise: false);
canvas.drawPath(path, paint);
canvas.translate(0, 150);
path.reset();
path.moveTo(0, 0);
path.lineTo(80, -40);
path.arcToPoint(Offset(80, 40),
radius: Radius.circular(60), largeArc: true, clockwise: false);
canvas.drawPath(path, paint);
canvas.translate(0, 150);
path.reset();
path.moveTo(0, 0);
path.lineTo(80, -40);
path.arcToPoint(Offset(80, 40),
radius: Radius.circular(60), largeArc: false, clockwise: true);
canvas.drawPath(path, paint);
canvas.translate(0, 150);
path.reset();
path.moveTo(0, 0);
path.lineTo(80, -40);
path.arcToPoint(Offset(80, 40),
radius: Radius.circular(60), largeArc: true, clockwise: true);
canvas.drawPath(path, paint);
}
- void relativeArcToPoint(Offset arcEndDelta, { Radius radius = Radius.zero, double rotation = 0.0, bool largeArc = false, bool clockwise = true, }) 指定一个相对当前点的位置点,从当前path的末端画一个弧到该点 - largeArc,是否使用优弧, true 优弧,false 劣弧 - clockwise,是否顺时针画 - 这两个参数一块儿理解,顺时针画劣弧,顺时针画优弧,逆时针画劣弧,逆时针画优弧
void _drawRelativeArttoPoint(Canvas canvas, Size size) {
Paint paint = new Paint();
paint
..color = Colors.redAccent[200]
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = 2;
canvas.translate(0, -300);
Path path = Path();
path.moveTo(0, 0);
path.lineTo(80, -40);
path.relativeArcToPoint(Offset(0, 80),
radius: Radius.circular(60), largeArc: false, clockwise: false);
canvas.drawPath(path, paint);
canvas.translate(0, 150);
path.reset();
path.moveTo(0, 0);
path.lineTo(80, -40);
path.relativeArcToPoint(Offset(0, 40),
radius: Radius.circular(60), largeArc: true, clockwise: false);
canvas.drawPath(path, paint);
canvas.translate(0, 150);
path.reset();
path.moveTo(0, 0);
path.lineTo(80, -40);
path.relativeArcToPoint(Offset(0, 40),
radius: Radius.circular(60), largeArc: false, clockwise: true);
canvas.drawPath(path, paint);
canvas.translate(0, 150);
path.reset();
path.moveTo(0, 0);
path.lineTo(80, -40);
path.relativeArcToPoint(Offset(0, 40),
radius: Radius.circular(60), largeArc: true, clockwise: true);
canvas.drawPath(path, paint);
}
贝塞尔线段
添加从当前点到曲线的贝塞尔线段。
- void conicTo(double x1, double y1, double x2, double y2, double w) 给定点(x2,y2),使用控制点(x1,y1)和权重w:
- 如果权重大于1,则曲线为双曲线;
- 如果重量等于1,它就是抛物线;
- 如果是小于1,它是一个椭圆。
相当于当前path末端、(x1,y1)、(x2 y2) 和w,共同控制一条曲线的生成。w越大,曲线越尖。
void _drawAconicTo(Canvas canvas, Size size) {
final Offset p1 = Offset(80, -150);
final Offset p2 = Offset(160, 0);
Path path = Path();
Paint paint = Paint()
..color = Colors.purpleAccent
..strokeWidth = 2
..style = PaintingStyle.stroke;
path.moveTo(0, 0);
path.lineTo(0, -50);
//抛物线
path.conicTo(p1.dx, p1.dy, p2.dx, p2.dy, 3);
canvas.translate(0, -150);
canvas.drawPath(path, paint);
path.reset();
path.moveTo(0, 0);
path.lineTo(0, -50);
path.conicTo(p1.dx, p1.dy, p2.dx, p2.dy, 1);
canvas.translate(0, 150);
canvas.drawPath(path, paint);
path.reset();
path.moveTo(0, 0);
path.lineTo(0, -50);
path.conicTo(p1.dx, p1.dy, p2.dx, p2.dy, 0.5);
canvas.translate(0, 150);
canvas.drawPath(path, paint);
}
- void relativeConicTo(double x1, double y1, double x2, double y2, double w) 和conicTo一样,只是点的描述从绝对位置变为了相对位置
画贝塞尔曲线
- void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) 添加从当前点到给定点(x3,y3)的三次贝塞尔曲线段,使用控制点(x1,y1)和(x2,y2)。
- void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) 画三次贝塞尔曲线 相对位置
- void quadraticBezierTo(double x1, double y1, double x2, double y2) 使用控制点(x1,y1)添加从当前点到给定点(x2,y2)的二次贝塞尔曲线段。
- void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) 画二次贝塞尔曲线 相对位置
void _testCubicTo(Canvas canvas, Size size) {
final Offset p1 = Offset(80, -150);
final Offset p2 = Offset(160, 0);
final Offset p3 = Offset(160, -160);
Path path = Path();
Paint paint = Paint()
..color = Colors.purpleAccent
..strokeWidth = 2
..style = PaintingStyle.stroke;
path.moveTo(0, 0);
path.lineTo(0, -50);
//抛物线
path.quadraticBezierTo(p1.dx, p1.dy, p2.dx, p2.dy);
canvas.translate(0, -150);
canvas.drawPath(path, paint);
path.reset();
path.moveTo(0, 0);
path.lineTo(0, -50);
path.cubicTo(p1.dx, p1.dy, p2.dx, p2.dy,p3.dx,p3.dy);
canvas.translate(0, 250);
canvas.drawPath(path, paint);
}
画形状
path add形状后,与当前path没有连接关系,所add的图形都是一个个独立的形状。
- void addArc(Rect oval, double startAngle, double sweepAngle) 添加一个弧形
- void addOval(Rect oval) 添加一个椭圆
- void addPath(Path path, Offset offset, {Float64List? matrix4}) 添加一个新的子路径,该子路径由给定的“path”偏移量和给定的“offset”组成。如果指定了“matrix4”,则在将矩阵转换为给定偏移量后,该路径将由该矩阵转换。矩阵是按列主顺序存储的4x4矩阵。
- void extendWithPath(Path path, Offset offset, {Float64List? matrix4}) 在路径最后再添加一个其他路径
- void addPolygon(List points, bool close) 添加一段折线
- void addRect(Rect rect) 添加一个矩形
- void addRRect(RRect rrect) 添加一个圆角矩形
void _testAdd(Canvas canvas, Size size) {
Path path = Path();
Paint paint = Paint()
..color = Colors.purpleAccent
..strokeWidth = 2
..style = PaintingStyle.stroke;
path.moveTo(0, 0);
path.lineTo(0, -50);
path.addRect(Rect.fromPoints(Offset(50,50), Offset(100,100)));
path.addRRect(RRect.fromRectXY(Rect.fromPoints(Offset(50,-150), Offset(100,-50)),10,10));
path.addOval(Rect.fromPoints(Offset(150,50), Offset(180,100)));
Path path2 = Path();
path2.moveTo(0, 0);
path2.addArc(Rect.fromPoints(Offset(0,0), Offset(-180,180)), 0, pi);
path.addPath(path2, Offset(0,100));
// canvas.translate(-150, -150);
canvas.drawPath(path, paint);
}
矩阵处理
注意调用方法后返回一个新的path,原来的path不变:
- Path transform(Float64List matrix4) 返回一个新的path,该path是原path做完矩阵变化后的处理 应用的是矩阵的平移、缩放、旋转等操作。
- Path shift(Offset offset) 返回一个新的path,该path是原path平移后的处理。可以理解为trasform的一个平移快捷方式方式
void _testTransform(Canvas canvas, Size size) {
Path path = Path();
Paint paint = Paint()
..color = Colors.purpleAccent
..strokeWidth = 2
..style = PaintingStyle.stroke;
path.moveTo(0, 0);
path.lineTo(0, -50);
path.lineTo(-50, -50);
Path path2 = path.transform(Float64List.fromList([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
-100, -100, 0, 1,
]));
canvas.drawPath(path, paint);
canvas.drawPath(path2, paint);
}
路径操作
- close() 将路径起始点与终点连接在一起
- reset() 路径清空
- PathMetrics computeMetrics({bool forceClosed = false}) 获得路径轮廓,指的是从第一笔到路径最后一笔过程中的各种操作,比如lineto addArc等。moveTo不算在内。可以理解为是诸多操作的集合。可以进行遍历,遍历的每个item是一个操作的描述信息
- PathMetrics foreach后,每个item是一个PathMetric
- PathMetric
- PathMetric.length,当前操作的路径总长度
- PathMetric.getTangentForOffset(),获取某一操作的路径上的具体的点的信息,参数值范围 0-PathMetric.length;
- Tangent t = pm.getTangentForOffset(pm.length * 0.5) Tangent是当前路径某一点的信息
- position,点的位置信息
- angle,点的角度信息
- bool contains(Offset point) 判断一个点是不是在路径上
- Rect getBounds() 反馈当前路径的最外层矩形
PathMetrics示例,在每个操作的路径一半的位置画一个点,下面有四个操作,因此有四个点:
void _testComputeMetrics(Canvas canvas, Size size) {
Path path = Path();
Paint paint = Paint()
..color = Colors.purpleAccent
..strokeWidth = 2
..style = PaintingStyle.stroke;
path.moveTo(0, 0);
path.lineTo(0, -50);
path.addRect(Rect.fromPoints(Offset(50,50), Offset(100,100)));
path.addRRect(RRect.fromRectXY(Rect.fromPoints(Offset(50,-150), Offset(100,-50)),10,10));
path.addOval(Rect.fromPoints(Offset(150,50), Offset(180,100)));
PathMetrics pms = path.computeMetrics(forceClosed: false);
pms.forEach((pm) {
Tangent t = pm.getTangentForOffset(pm.length * 0.5);
canvas.drawCircle(
t.position, 5, Paint()..color = Colors.black);
});
canvas.drawPath(path, paint);
}
一个乱七八糟的形状,🤦
重点看底下的代码使用,形状就算了,惭愧!!!
void _drawPath(Canvas canvas, Size size) {
Paint paint = new Paint();
paint
..color = Colors.redAccent[200]
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = 4;
Path path = Path();
path.moveTo(0, 0);
path.lineTo(80, 80);
path.relativeLineTo(0, -80);
path.arcTo(Rect.fromPoints(Offset(0,-50), Offset(100,-80)), 0, pi/2, false);
path.arcToPoint(Offset(0,-100),largeArc: true);
path.relativeArcToPoint(Offset(90,-90));
path.conicTo(200, -200, 100, -100, 1);
path.relativeConicTo(200, -200, 100, -100, 1);
path.cubicTo(0, -100, -100, 0, -100, 100);
path.relativeCubicTo(0, -100, -100, 0, -100, 100);
path.quadraticBezierTo(100, 100, 200, 100);
path.relativeQuadraticBezierTo(-100, 100, 0, 100);
path.addArc(Rect.fromPoints(Offset(0,-50), Offset(100,-80)), 0, pi);
path.addOval(Rect.fromPoints(Offset(0,-50), Offset(100,-80)));
path.addPath(Path()..lineTo(100, 100), Offset.zero);
path.addPolygon([Offset(0,0),Offset(100,100)], false);
path.addRect(Rect.fromPoints(Offset(0,-50), Offset(100,-80)));
path.addRRect(RRect.fromRectXY(Rect.fromPoints(Offset(0,-50), Offset(100,-80)), 100, 200));
PathMetrics pm = path.computeMetrics();
bool iscontaint = path.contains(Offset(0,0));
print("iscontain = "+iscontaint.toString());
path.extendWithPath(Path()..moveTo(-100, -200)..lineTo(100,100), Offset.zero);
path.close();
canvas.drawPath(path, paint);
Rect bound = path.getBounds();
canvas.drawRect(bound, paint);
canvas.drawPath(path.transform(Float64List.fromList([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, -200, 0, 1,
])), paint);
canvas.drawPath(path.shift(Offset(-100,0)), paint);
// path
// ..relativeMoveTo(0, 0)
// ..relativeLineTo(100, 120)
// ..relativeLineTo(-10, -60)
// ..relativeLineTo(
// 60,
// -10,
// )
// ..close();
// canvas.drawPath(path, paint);
// path.reset();
// path
// ..relativeMoveTo(-200, 0)
// ..relativeLineTo(100, 120)
// ..relativeLineTo(-10, -60)
// ..relativeLineTo(
// 60,
// -10,
// )
// ..close();
//
// canvas.drawPath(path, paint);
}