原文:Flutter Canvas学习之绘图篇 - JSShou的文章 - 知乎
https://zhuanlan.zhihu.com/p/376329260
绘制点-drawPoints
void drawPoints(PointMode pointMode, List points, Paint paint)
pointMode: 设置点、线
PointMode.points 设置点
PointMode.lines 两个两个点之间连接,如果传入的points是奇数,最后一个点将会被忽略
PointMode.polygon 将所有点连接起来
points: 一个Offset数组,可以画多个点
如果paint
设置了strokeCap = StrokeCap.round
,画的点将是圆形(其它任何API画的点、线都同理,都跟随画笔属性)
import 'dart:ui' as ui;
//...
// 画点
Paint paint = Paint()
..color = Colors.red
..strokeWidth = 20;
canvas.drawPoints(
ui.PointMode.points,
[
Offset(100, 100),
Offset(250, 180),
Offset(200, 300),
],
paint);
// 将端点设置为圆形
paint.strokeCap = StrokeCap.round;
canvas.drawPoints(ui.PointMode.points, [Offset(100, 200)], paint);
如果将入参都设置为PointMode.polygon
绘制线段-drawLine
void drawLine(Offset p1, Offset p2, Paint paint)
- p1,p2表示两个端点的位置
canvas.drawLine(Offset(100, 100), Offset(250,180), paint);
drawLine
只能用于绘制一条线段,drawPoints
可以绘制同样效果
canvas.drawPoints(
ui.PointMode.lines,
[
Offset(100, 100),
Offset(250, 180),
],
paint);
绘制区域-drawRect
因为很多方法都会用到Rect
,所以这个我会把drawRect
方法放到最开头讲。
Rect
表示绘制一个矩形区域,它没有构造函数,它只有一些静态方法
fromLTRB
Rect Rect.fromLTRB(double left, double top, double right, double bottom)
此方法是所有该类方法的母本,其它方法都是使用此方法实现的
- left: 矩形左边距离画布左边距离
- top: 矩形顶部距离画布顶部距离
- right: 矩形右边距离画布左边边距离
- bottom: 矩形底部距离画布顶部距离
下面是一个例子
canvas.drawRect(Rect.fromLTRB(50, 50, 350, 350), paint);
fromCenter
Rect.fromCenter({Offset center, double width, double height})
画一个长方形
- center: 长方形方形中心点位置
- width: 长方形的宽
- height: 长方形的高
canvas.drawRect(
Rect.fromCenter(center: Offset(200,300), width: 250, height: 350), paint);
fromCircle
Rect.fromCircle({Offset center, double radius})
画一个正方形
- center: 正方形中心点位置
- radius: 正方形四条边距离中心点距离
canvas.drawRect(
Rect.fromCircle(center: Offset(200, 300), radius: 150), paint);
fromPoints
Rect.fromPoints(Offset a, Offset b)
使用两个点确定一个矩形
- a: 矩形左上角的位置
- b: 矩形右下角的位置
canvas.drawRect(Rect.fromPoints(Offset(100, 200), Offset(300, 400)), paint);
绘制圆角矩形-RRect
void drawRRect(RRect rrect, Paint paint)
RRect用来绘制带圆角的矩形,其绘制的位置原理同Rect.fromLTRB
一样,只是多了一个设置圆角的参数。
RRect rRect = RRect.fromLTRBR(100, 100, 350, 350, Radius.circular(30));
canvas.drawRRect(rRect, paint);
绘制圆-drawOval
void drawOval(Rect rect, Paint paint)
drawOval
用于绘制圆形
Rect pRect = Rect.fromLTRB(50, 150, 400, 350);
// 为了区别,先绘制一个矩形区域
canvas.drawRect(pRect, paint);
paint.color = Colors.yellow;
// 绘制椭圆
canvas.drawOval(pRect, paint);
黄色区域就是绘制的圆,它就是在Rect中进行绘制的
绘制圆弧-drawArc
drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
绘制圆弧,useCenter表示是否绘制中心点到圆弧两边
Rect rect = Rect.fromCircle(
center: Offset(size.width / 2, size.height / 2), radius: 100);
// 为了方便查看,把区域也绘制出来
canvas.drawRect(rect);
//绘制圆弧
canvas.drawArc(rect, 90 * (pi / 180), 90 * (pi / 180), false, paint);
有关角度的介绍,请看我上篇文章
绘制路径-drawPath
void drawPath(Path path, Paint paint)
这个厉害了,drawPath
是一个很强大的方法,它可以说是canvas
的精髓,几乎所以的其它绘制都可以用它来绘制。其它方法可以简略看看,这个一定要重点学习理解。下面来看看它的能力
绘制简单的形状
Path path = new Path();
path.moveTo(100, 100);
path.lineTo(200, 200);
path.lineTo(250, 180);
path.lineTo(200, 300);
path.lineTo(100, 200);
canvas.drawPath(path, paint);
Path
有很多方法,下面介绍常用方法
moveTo
设置画笔开始的位置
lineTo
绘制的下一个位置,传入的是相对于坐标系的具体位置,会按照代码顺序进行移动绘制
relativeLineTo
与lineTo类似,不过传入的是相对于上一个点为原点的位置,比如上一个点是在(100,100),传入的是(150,150),如果用lineTo要达到同样效果应该传入(250,250)。它有很多方法,带了relative
的都是同样的原理,所以后续relative-
方法就不讲解了。
arcTo
绘制一个弧线
void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo)
此方法同drawArc类似,前三个参数都一样,最后一个参数表示是否跟path之前的绘制(如通过lineTo绘制的线段)相连,false表示连接,true表示不相连。
quadraticBezierTo
绘制二阶贝塞尔曲线
void quadraticBezierTo(double x1, double y1, double x2, double y2)
什么是塞尔曲线,维基百科这样介绍它
在数学的数值分析领域中,贝塞尔曲线(英语:Bézier curve)是计算机图形学中相当重要的参数曲线。更高维度的广泛化贝塞尔曲线就称作贝兹曲面,其中贝兹三角是一种特殊的实例。
它的绘制过程如下(来自维基百科):
二阶贝塞尔曲线的确立,需要三个点,P0开始点、P1过程点、P2结束点
使用quadraticBezierTo
来绘制时,我们可以通过moveTo
来确定开始点P0,x1、y1确定P1,x2、y2确定P2
var path = Path();
path.moveTo(50, 500);
path.quadraticBezierTo(100, 300, 350, 300);
canvas.drawPath(path1, paint);
其中蓝色的坐标系、黄色的点是我画的辅助线,红色即是上面代码执行结果
conicTo
void conicTo(double x1, double y1, double x2, double y2, double w
同样也是绘制二阶贝塞尔曲线,但是同quadraticBezierTo
相比,它多了一个参数w
,用于控制曲线的弧度。当 w < 1 时,曲线弧度更小;w = 1 时同quadraticBezierTo
效果一样;w > 1 时,弧度更大
path.conicTo(100, 300, 350, 300, 3);
cubicTo
绘制三阶贝塞尔曲线
void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3)
三阶贝塞尔曲线相比二阶贝塞尔曲线只是多了一个点,原理类似
path.moveTo(50, 500);
path.cubicTo(50, 200, 300, 400, 350, 150);
addRect
绘制一个矩形区域
path.addRect(Rect.fromLTRB(50, 50, 350, 350));
此效果跟上面drawRect中第一个例子一样
addRRect
绘制一个带圆角的矩形
RRect rRect = RRect.fromLTRBR(100, 100, 350, 350, Radius.circular(30));
path.addRRect(rRect);
上面代码效果跟drawRRect一样
addArc
绘制一个圆弧
Path path = new Path();
// 画一个矩形区域
Rect rect = Rect.fromCircle(
center: Offset(size.width / 2, size.height / 2), radius: 100);
canvas.drawRect(rect, paint);
// 在矩形区域画圆弧
path.addArc(rect, 90 * (pi / 180), 90 * (pi / 180));
paint.color = Colors.red;
canvas.drawPath(path, paint);
上面代码同drawArc效果一样
canvas.drawArc
、path.addArc
、path.arcTo
(当forceMoveTo为true时),三种方式都能绘制同样的圆弧效果
addOval
绘制一个椭圆
Rect pRect = Rect.fromLTRB(50, 150, 400, 350);
path.addOval(pRect);
canvas.drawPath(path, paint);
代码效果同drawOval
addPolygon
通过点绘制线段
void addPolygon(List points, bool close)
- point: 传入多个点的位置
- close: 为true时最后一个点会和第一个点相连
Path path = new Path();
path.addPolygon([
Offset(100, 100),
Offset(250, 180),
Offset(200, 300),
], false);
canvas.drawPath(path, paint);
computeMetrics
PathMetrics computeMetrics({bool forceClosed = false})
computeMetrics
方法用于返回一个之前绘制的路径的一份快照。当我们使用moveTo
、lineTo
、arcTo
、conicTo
等绘制路径时,可以使用此来实现只绘制其中一部分。
比如:
var path = Path();
path.moveTo(50, 500);
path.cubicTo(50, 200, 300, 400, 350, 150);
// 将完整绘制图形置为红色
paint.color = Colors.red;
canvas.drawPath(path, paint);
ui.PathMetrics pathMetrics = path.computeMetrics();
// 绘制一半
var progress = 0.5;
// 将颜色更改为紫色用于区分
paint.color = Colors.deepPurple;
for (ui.PathMetric pathMetric in pathMetrics) {
Path extractPath = pathMetric.extractPath(
0.0,
pathMetric.length * progress,
);
canvas.drawPath(extractPath, paint);
}
extendWithPath
void extendWithPath(Path path, Offset offset, {Float64List matrix4})
用于复制一份之前绘制的路径并平移offset
的位置,原路径会和新路径连接。matrix4
是对新路径进行一个4D矩阵处理。
var path = Path();
path.moveTo(50, 500);
// 绘制一个三阶贝塞尔曲线
path.cubicTo(50, 200, 300, 400, 350, 150);
// 处理
path.extendWithPath(path, Offset(50, 30),
matrix4: Float64List.fromList(
[1, 0, 0, 0, .1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2]));
canvas.drawPath(path, paint);
图中蓝色圈起来的部分就是方法的效果
shift
Path shift(Offset offset)
可用于复制之前绘制的路径并平移offset
位置,返回Path
。与extendWithPath
不同的是,此方法仅仅是复制路径,不会跟原路径相连。
var path = Path();
path.moveTo(50, 500);
path.cubicTo(50, 200, 300, 400, 350, 150);
// 原始图形为红色
paint.color = Colors.red;
canvas.drawPath(path, paint);
// 复制原路径并平移
var path2 = path.shift(Offset(50,20));
paint.color = Colors.yellow;
canvas.drawPath(path2, paint);
close
void close()
用于将路径起点与终点连接起来
总结
经过上面学习,我们知道了其实多种绘制方式能达到一样的效果
绘制点
- canvas.drawPoints
- path.addPolygon
绘制直线
- drawPoints传参
PointMode.polygon
或PointMode.lines
- drawLine
- path.lineTo
绘制圆弧
- canvas.drawArc
- path.addArc