[1]. 了解如何通过移动路径形成形状:直线移动、圆弧移动、圆锥曲线移动、贝塞尔曲线移动。
[2]. 了解路径的 [绝对移动] 和 [相对移动]。
[3]. 了解在已有的路径中添加其他形状:添加矩形、圆角矩形、椭圆、圆弧、多边形、其他路径。
[4]. 使用 path 绘制坐标系。
一、路径加入方法
下图是路径形成的基础方法,包括路径的
移动
、加入直线
、圆弧
、圆锥曲线
、贝塞尔曲线
。
对这些 API 的掌握程度,直接决定你运用路径的能力。
1.moveTo
和lineTo
: 画线
下面画布已经移动到
屏幕中心
,并且 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.relativeMoveTo
和relativeLineTo
: 相对画线
如果
点位已经知道
,使用 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.arcToPoint
和relativeArcToPoint
: 点定弧
当想要
画圆弧到某个点
,用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.conicTo
和relativeConicTo
: 圆锥曲线
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.quadraticBezierTo
和relativeQuadraticBezierTo
: 二阶贝塞尔
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.cubicTo
和relativeCubicTo
: 三阶贝塞尔
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. addRect
和addRRect
: 添加类矩形
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. addOval
和addArc
: 添加类圆形
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;
}
到这里,基本的路径添加操作就介绍完了,下一节将介绍路径的操作方法。