Flutter学习:使用CustomPaint绘制图形
Flutter学习:认识CustomPaint组件和Paint对象
Flutter学习:使用CustomPaint绘制路径
Flutter学习:使用CustomPaint绘制图形
Flutter学习:使用CustomPaint绘制文字
Flutter学习:使用CustomPaint绘制图片
Canvas
canvas绘制的所有内容都是以屏幕左上角为原点,右边和下边为正方向延伸的直角坐标系。
drawCircle 绘制圆形
drawCircle需要传递3个参数:
Offset c
:圆心的位置坐标double radius
:圆的半径Paint paint
:绘制对象
// 以左上角为圆心绘制
canvas.drawCircle(const Offset(0, 0), 100, paint);
// 以中心点为圆心绘制
canvas.drawCircle(size.center(Offset.zero), 100, paint);
// 以右下角为圆心绘制
canvas.drawCircle(Offset(size.width, size.height), 100, paint);![CustomPaint_drawCircle]
drawRect 绘制矩形
drawCircle需要传递2个参数:
Rect rect
:Rect对象Paint paint
:绘制对象
创建Rect对象的方法有7种:
Rect.zero
绘制一个左、上、右和下边缘都为零的矩形
Rect rect = Rect.zero;
canvas.drawRect(rect, paint);
Rect.largest
绘制一个覆盖整个坐标空间的矩形
Rect rect = Rect.largest;
canvas.drawRect(rect, paint);
Rect.fromCenter
确定一个矩形的中心点坐标来绘制。
Rect.fromCenter({ required Offset center, required double width, required double height})
需要传递3个参数:
- center用来设置矩形的中心点坐标
- width用来设置矩形的宽
- height用来确定矩形的高
Offset center = size.center(Offset.zero);
Rect rect = Rect.fromCenter(center: center, width: 250, height: 250);
canvas.drawRect(rect, paint);
Rect.fromLTRB
从左、上、右和下边缘构造一个矩形
Rect.fromLTRB( double left, double top, double right, double bottom)
需要传递4个参数:
- left代表左上角顶点的x坐标
- top代表左上角顶点的y坐标
- right代表右下角顶点的x坐标
- bottom代表右下角顶点的y坐标
Rect rect = const Rect.fromLTRB(50, 300, 350, 500);
canvas.drawRect(rect, paint);
Rect.fromCircle
构造一个以给定圆为边界的矩形
Rect.fromCircle({required Offset center, required double radius})
需要传递2个对象:
- center用来设置圆的圆心左标
- radius用来设置圆的半径
Offset center = size.center(Offset.zero);
Rect rect = Rect.fromCircle(center: center, radius: size.width / 3);
canvas.drawRect(rect, paint);
Rect.fromLTWH
通过左上角点的坐标和宽度、高度来构造一个矩形
Rect.fromLTWH( double left, double top, double width, double height)
需要传递4个参数:
- left用来设置左上角的x坐标
- top用来设置左上角的y坐标
- width用来设置矩形的宽
- height用来设置矩形的高
Rect rect = const Rect.fromLTWH(50, 220, 300, 300);
canvas.drawRect(rect, paint);
Rect.fromPoints
通过两个坐标点来绘制矩形。
Rect.fromPoints(Offset a, Offset b)
需要传递两个Offset对象:
- a代表左上角点的坐标
- b代表右下角的点的坐标
Rect rect = Rect.fromPoints(const Offset(60, 200), const Offset(320, 500));
canvas.drawRect(rect, paint);
drawRRect 绘制圆角矩形
RRect.zero
绘制一个左、上、右和下边缘都为零的圆角矩形,和Rect.zero效果一样
RRect rrect = RRect.zero;
RRect.fromRectAndRadius
通过绘制一个矩形再设置圆角半径来绘制圆角矩形
RRect RRect.fromRectAndRadius(Rect rect, Radius radius)
需要传递2个参数:
- rect是一个矩形对象
- radius为矩形设置圆角
Rect rect = Rect.fromPoints(const Offset(50, 200), const Offset(320, 600));
RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(40.0));
canvas.drawRRect(rrect, paint);
RRect.fromLTRBR
RRect.fromLTRBR(double left, double top, double right, double bottom, Radius radius)
需要传递5个参数,前4个参数和Rect.fromLTRB一样:
- left代表左上角顶点的x坐标
- top代表左上角顶点的y坐标
- right代表右下角顶点的x坐标
- bottom代表右下角顶点的y坐标
- radius用来设置矩形的圆角半径
RRect rrect = RRect.fromLTRBR(80, 120, 320, 420, const Radius.circular(48));
canvas.drawRRect(rrect, paint);
RRect.fromLTRBXY
RRect.fromLTRBXY( double left, double top, double right, double bottom, double radiusX, double radiusY)
需要传递6个参数,前4个参数和Rect.fromLTRB一样:
- left代表左上角顶点的x坐标
- top代表左上角顶点的y坐标
- right代表右下角顶点的x坐标
- bottom代表右下角顶点的y坐标
- radiusX用来设置x方向的半径长度
- radiusY用来设置y方向的半径长度
RRect rrect = const RRect.fromLTRBXY(50, 150, 320, 530, 90, 60);
canvas.drawRRect(rrect, paint);
RRect.fromLTRBAndCorners
从其左、上、右和下边缘以及 topLeft、topRight、bottomRight 和 bottomLeft 半径构造一个圆角矩形
RRect.fromLTRBAndCorners(
double left, double top, double right, double bottom, {
Radius topLeft = Radius.zero,
Radius topRight = Radius.zero,
Radius bottomRight = Radius.zero,
Radius bottomLeft = Radius.zero
})
可以传递8个参数,其中4个为必选的参数:
- left代表左上角顶点的x坐标
- top代表左上角顶点的y坐标
- right代表右下角顶点的x坐标
- bottom代表右下角顶点的y坐标
- topLeft用来设置左上角的圆角半径
- topRight用来设置右上角的圆角半径
- bottomRight用来设置右下角角的圆角半径
- bottomLeft用来设置左下角的圆角半径
RRect rrect = RRect.fromLTRBAndCorners(50, 120, 320, 420,
topLeft: const Radius.circular(20),
topRight: const Radius.circular(40),
bottomLeft: const Radius.circular(60),
bottomRight: const Radius.circular(80));
canvas.drawRRect(rrect, paint);
RRect.fromRectXY
根据方法名可以看出,需要依据一个矩形来绘制
RRect.fromRectXY( Rect rect, double radiusX, double radiusY)
需要传递3个参数:
-
rect用来绘制一个矩形
-
radiusX用来设置x方向的半径长度
-
radiusY用来设置y方向的半径长度
Rect rect = Rect.fromPoints(const Offset(50, 180), const Offset(300, 420));
RRect rrect = RRect.fromRectXY(rect, 60.0, 100.0);
canvas.drawRRect(rrect, paint);
RRect.fromRectAndCorners
RRect.fromRectAndCorners( Rect rect,{ Radius topLeft = Radius.zero, Radius topRight = Radius.zero, Radius bottomRight = Radius.zero, Radius bottomLeft = Radius.zero})
可以传递5个参数,1个必要的,4个可选的:
- rect用来绘制一个矩形
- bottom代表右下角顶点的y坐标
- topLeft用来设置左上角的圆角半径
- topRight用来设置右上角的圆角半径
- bottomRight用来设置右下角角的圆角半径
- bottomLeft用来设置左下角的圆角半径
Rect rect = Rect.fromPoints(const Offset(40, 125), const Offset(330, 520));
RRect rrect = RRect.fromRectAndCorners(rect,
topLeft: const Radius.circular(60),
topRight: const Radius.circular(40),
bottomLeft: const Radius.circular(80),
bottomRight: const Radius.circular(20));
canvas.drawRRect(rrect, paint);
drawArc 绘制圆弧
drawArc绘制一个以矩形为参照物的圆弧,需要传递5个属性:
Rect rect
:矩形的位置和大小double startAngle
:圆弧开始的角度double sweepAngle
:圆弧开始到结束的角度大小bool useCenter
:是否向中心闭合Paint paint
:绘制对象
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
// 闭合
canvas.drawArc(rect, 0, 3.14, true, paint);
// 不闭合
canvas.drawArc(rect, 0, 3.14, false, paint);
drawColor 绘制颜色
drawColor绘制的颜色会沾满子整个屏幕。需要传递2个参数:
Color color
:要绘制的颜色BlendMode blendMode
:颜色的混合模式
canvas.drawColor(Colors.blue, BlendMode.darken);
drawShadow绘制阴影
绘制阴影
drawShadow(Path path, Color color, double elevation, bool transparentOccluder)
需要传递4个参数:
- path是需要绘制阴影的路径
- color是阴影的颜色
- elevation是阴影的范围
- transparentOccluder表示如果遮挡对象是透明的,应该为true,否则为false
Path path = Path();
path.moveTo(80, 200);
path.lineTo(320, 400);
path.lineTo(200, 340);
path.lineTo(100, 460);
path.close();
canvas.drawShadow(path, Colors.black, 8.0, false);
canvas.drawPath(path, paint);
drawVertices
绘制顶点。需要传递3个参数:
Vertices vertices
:需要绘制的顶点对象BlendMode blendMode
:混合模式Paint paint
:绘制对象
该方法的重点在于Vertices
对象,它有5个参数:
VertexMode mode
:顶点模式List positions
:设置所有顶点的坐标List<Offset>? textureCoordinates
:用于裁剪图像着色器中设置的图像。切割部分应用于三角形网格。请注意, textureCoordinates是图像上的坐标List<Color>? colors
:设置每个顶点处的颜色,数量必须和positions一样List<int>? indices
:如果提供了indices参数,则列表中的所有值都必须是positions的有效索引值
先来绘制一个最简单的:
Paint paint = Paint()..color = Colors.blue;
Vertices vertices = Vertices(
VertexMode.triangles,
const [ Offset.zero, Offset(-100, 120), Offset(60, 60)],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);
因为我的CustomPaint
是包裹在Center
组件中,所以它以中心为原点。如果没有Center
组件,会以左上角为原点。
VertexMode
我们来研究一下VertexMode
这个枚举类:
VertexMode.triangles
:将三个点的每个序列绘制为三角形的顶点VertexMode.triangleFan
:绘制第一个点和两个点的每个滑动窗口作为三角形的顶点VertexMode.triangleStrip
:将三个点的每个滑动窗口绘制为三角形的顶点
这几句是官方文档写的,看起来很难理解,所以我引用【Flutter 专题】35 图解自定义 View 之 Canvas (三)这篇文章关于这几个值的描述,借用 A B C D E F G H I 来代替顶点:
VertexMode.triangles
:每三个分割顶点相连,即 [A B C] [D E F] [G H I] 共 3 组VertexMode.triangleStrip
:每相邻的三个顶点相连,即 [A B C] [B C D] [C D E] [D E F] [E F G] [F G H] [G H I] 共 7 组VertexMode.triangleFan
:每相邻的两个顶点与首点相连,即 [A B C] [A C D] [A D E] [A E F] [A F G] [A G H] [A H I] 共 7 组
试用一下VertexMode.triangleStrip
:
Paint paint = Paint()..color = Colors.blue;
Vertices vertices = Vertices(
VertexMode.triangleStrip,
const [Offset(-200, -80), Offset.zero, Offset(-100, 120), Offset(60, 60)],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);
试用一下VertexMode.triangleFan
:
Paint paint = Paint()..color = Colors.blue;
Vertices vertices = Vertices(
VertexMode.triangleFan,
const [Offset(-200, -80), Offset.zero, Offset(-100, 120), Offset(60, 60), Offset(160, -120)],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);
colors
设置每个顶点处的颜色,数量必须和positions一样
Paint paint = Paint();
Vertices vertices = Vertices(
VertexMode.triangleFan,
const [Offset(-200, -80), Offset.zero, Offset(-100, 120), Offset(60, 60), Offset(160, -120)],
colors: [Colors.red, Colors.orange, Colors.blue, Colors.yellow, Colors.green],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);
indices
用来过滤positions的索引
Paint paint = Paint()..color = Colors.blue;
Vertices vertices = Vertices(
VertexMode.triangleFan,
const [Offset(-200, -80), Offset.zero, Offset(-100, 120), Offset(60, 60), Offset(160, -120)],
indices: [0, 1, 4],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);
textureCoordinates
裁剪图像着色器绘制的图片,数量必须和positions一样
canvas.drawImage(image, Offset.zero, Paint());
Paint paint = Paint()
..color = Colors.blue
..shader = ImageShader(image, TileMode.decal, TileMode.decal, Matrix4.skewX(0).storage);
Vertices vertices = Vertices(
VertexMode.triangleFan,
[
Offset.zero,
Offset(size.width, 0),
Offset(0, size.height),
Offset(size.width, size.height),
Offset(size.width, 0),
],
textureCoordinates: [
Offset.zero,
Offset(size.width - 100, 0),
Offset(0, size.height - 100),
Offset(size.width - 80, size.height - 80),
Offset(size.width - 40, 0),
],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);
drawDRRect 绘制嵌套圆角矩形
drawDRRect绘制嵌套的两个矩形,outer圆角矩形的宽高必须大于等于inner圆角矩形的宽高。需要传递3个参数:
RRect outer
:绘制外围圆角矩形RRect inner
:绘制内部圆角矩形Paint paint
:绘制对象
// 外围圆角矩形
Rect rectOuter = Rect.fromCenter(center: size.center(Offset.zero), width: 300, height: 300);
RRect outer = RRect.fromRectAndRadius(rectOuter, const Radius.circular(80));
// 内部圆角矩形
Rect rectInner = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 100);
RRect inner = RRect.fromRectAndRadius(rectInner, const Radius.circular(60));
canvas.drawDRRect(outer, inner, paint);
drawLine 绘制线段
drawLine需要传递3个参数:
Offset p1
:第一个点的位置Offset p2
:第二个点的位置Paint paint
:绘制对象
Offset p1 = const Offset(200, 50);
Offset p2 = const Offset(600, 200);
canvas.drawLine(p1, p2, paint);
drawOval 绘制椭圆
drawOval绘制一个轴对齐的椭圆。需要传递2个参数:
Rect rect
:获取椭圆的原点和宽高Paint paint
:绘制对象
// 宽高不相等为椭圆
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 300, height: 200);
canvas.drawOval(rect, paint);
// 宽高相等为正圆
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 300, height: 300);
canvas.drawOval(rect, paint);
drawPoints 绘制点
drawPoints根据给定的PointMode绘制一系列点。需要传递3个参数:
PointMode pointMode
:点的绘制模式List points
:点的坐标Paint paint
:绘制对象
List<Offset> points = const [
Offset(100, 100), Offset(200, 200),
Offset(100, 300), Offset(300, 400),
Offset(200, 500), Offset(300, 400),
];
// 绘制多条线段,两个点一组
canvas.drawPoints(PointMode.lines, points, paint);
// 绘制点,由strokeCap控制点的样式
canvas.drawPoints(PointMode.points, points, paint);
// 将每一个点连接起来
canvas.drawPoints(PointMode.polygon, points, paint);
drawRawPoints 绘制点
绘制一系列点,需要传入3个参数:
PointMode pointMode
:点的绘制模式Float32List points
:点的坐标Paint paint
:绘制对象
Paint paint = Paint()
..color = Colors.blue
..strokeWidth = 12;
Float32List points = Float32List.fromList([100, 120, 500, 250, 150, 300]);
canvas.drawRawPoints(PointMode.points, points, paint);
其他用法和drawPoints一样。
canvas.clipPath 裁剪路径
绘制一条路径,用来裁剪其他形状。有2个参数传递:
Path path
:绘制裁剪的形状bool doAntiAlias
:是否抗锯齿
// 裁剪的形状、路径
Path path = Path();
path.lineTo(-100, -100);
path.lineTo(-100, 200);
path.close();
canvas.clipPath(path);
// 裁剪前的原图形
Paint paint = Paint()..color = Colors.blue;
canvas.drawCircle(size.center(Offset.zero), 120, paint);
canvas.clipRect 裁剪矩形
绘制一个矩形,用来裁剪其他形状。有3个参数传递:
Rect rect
:绘制裁剪的形状ClipOp clipOp
:裁剪的类型bool doAntiAlias
:是否抗锯齿
// 裁剪的形状
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.clipRect(rect, clipOp: ClipOp.intersect);
// 裁剪前的原图形
Paint paint = Paint()..color = Colors.blue;
canvas.drawCircle(size.center(Offset.zero), 120, paint);
canvas.clipRRect 裁剪圆角矩形
绘制一个圆角矩形,用来裁剪其他形状。有2个参数传递:
RRect rrect
:绘制裁剪的形状bool doAntiAlias
:是否抗锯齿
// 裁剪的形状
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(80));
canvas.clipRRect(rrect);
// 裁剪前的原图形
Paint paint = Paint()..color = Colors.blue;
canvas.drawCircle(size.center(Offset.zero), 120, paint);
canvas.transform
对形状进行变形。只需要传递一个对象:
Float64List matrix4
:将当前变换乘以指定的 4⨉4 变换矩阵,该矩阵指定为以列优先顺序排列的值列表
Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.transform(Matrix4.rotationZ(1).storage);
canvas.drawRect(rect, paint);
canvas.rotate
旋转图形。只有一个传递的属性:
double radians
:旋转的角度,以3.14为标准
Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.rotate(1);
canvas.drawRect(rect, paint);
视图和canvas.transform的一样
canvas.scale
缩放图形。有2个参数可以传递:
double sx
:水平方向缩放double? sy
:垂直方向缩放
只写一个参数,第二个参数默认一样
Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.scale(1.3);
canvas.drawRect(rect, paint);
canvas.skew
倾斜图形。需要传递2个参数:
double sx
:围绕原点顺时针在运行单位上的水平倾斜double sy
:原点周围顺时针在运行单位上的垂直倾斜
两个参数的取值最好在 0-1 之间
Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.skew(.4, .2);
canvas.drawRect(rect, paint);
canvas.translate
偏移图形。需要传递2个参数:
double dx
:水平移动距离double dy
:垂直移动距离
Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.translate(40, 40);
canvas.drawRect(rect, paint);
canvas.save和canvas.restore
canvas可以把之前的属性存储起来,canvas.restore在把存储的属性释放出来
Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 150, height: 150);
canvas.save();
canvas.scale(.8, .8);
canvas.translate(150, 150);
canvas.drawRect(rect, paint);
canvas.restore();
canvas.drawRect(rect, paint);
save方法是将它之前的属性拷贝了一份存起来。restore是将存起来的那部分代码释放出来,就是把save之前的内容复制一份,放在restore的位置。
canvas.getSaveCount
返回保存堆栈上的项目数,包括初始状态。这意味着它为干净的画布返回 1,并且每次调用save和saveLayer都会增加它,并且每次匹配的restore调用都会减少它。这个数字不能低于 1。
canvas.saveLayer
相当于clipRect + save 两个方法。传递2个参数:
Rect? bounds
:绘制一个区域Paint paint
:画笔对象,主要是为了使用Paint.colorFilter和Paint.blendMode这两个属性
设置一个范围,图形只能在该范围显示,超出部分裁剪掉:
Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 150, height: 150);
canvas.drawRect(rect, paint);
Rect rect1 = const Rect.fromLTWH(100, 100, 200, 200);
canvas.drawRect(rect1, Paint()..color = Colors.red);
canvas.saveLayer(rect1, paint);
canvas.scale(.8, .8);
canvas.translate(150, 150);
canvas.drawRect(rect, paint);