绘制相关FlutterCanvasDart

Flutter Canvas学习会作为一个系列,原文传送门:

基于Flutter版本: 2.0.3

前言

上一篇讲了Flutter CustomPaint 组件相关的基本概念,这篇围绕canvas基础绘制能力展开。

绘制点-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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

points 如果将入参都设置为PointMode.polygon polygon

绘制线段-drawLine

void drawLine(Offset p1, Offset p2, Paint paint)

  • p1,p2表示两个端点的位置
canvas.drawLine(Offset(100, 100), Offset(250,180), paint);
1

drawLine

drawLine只能用于绘制一条线段,drawPoints可以绘制同样效果

canvas.drawPoints(
  ui.PointMode.lines,
  [
    Offset(100, 100),
    Offset(250, 180),
  ],
  paint);
1
2
3
4
5
6
7

绘制区域-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);
1

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);
1
2

fromCircle

Rect.fromCircle({Offset center, double radius})

画一个正方形

  • center: 正方形中心点位置
  • radius: 正方形四条边距离中心点距离
canvas.drawRect(
        Rect.fromCircle(center: Offset(200, 300), radius: 150), paint);
1
2

rect_from_circle

fromPoints

Rect.fromPoints(Offset a, Offset b)

使用两个点确定一个矩形

  • a: 矩形左上角的位置
  • b: 矩形右下角的位置
canvas.drawRect(Rect.fromPoints(Offset(100, 200), Offset(300, 400)), paint);
1

rect_from_point

绘制圆角矩形-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);
1
2

draw_rrect

绘制圆-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);
1
2
3
4
5
6

drawOval

黄色区域就是绘制的圆,它就是在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);
1
2
3
4
5
6

path_addrect

有关角度的介绍,请看我上篇文章

绘制路径-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);
1
2
3
4
5
6
7

drawPath

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)是计算机图形学中相当重要的参数曲线。更高维度的广泛化贝塞尔曲线就称作贝兹曲面,其中贝兹三角是一种特殊的实例。

它的绘制过程如下(来自维基百科):

bezier

二阶贝塞尔曲线的确立,需要三个点,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);
1
2
3
4

quadraticBezierTo

其中蓝色的坐标系、黄色的点是我画的辅助线,红色即是上面代码执行结果

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);
1

conicTo

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);
1
2

cubicTo

addRect

绘制一个矩形区域

path.addRect(Rect.fromLTRB(50, 50, 350, 350));
1

此效果跟上面drawRect中第一个例子一样

addRRect

绘制一个带圆角的矩形

RRect rRect = RRect.fromLTRBR(100, 100, 350, 350, Radius.circular(30));
path.addRRect(rRect);
1
2

上面代码效果跟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);
1
2
3
4
5
6
7
8
9

上面代码同drawArc效果一样

canvas.drawArcpath.addArcpath.arcTo(当forceMoveTo为true时),三种方式都能绘制同样的圆弧效果

addOval

绘制一个椭圆

Rect pRect = Rect.fromLTRB(50, 150, 400, 350);
path.addOval(pRect);
canvas.drawPath(path, paint);
1
2
3

代码效果同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);
1
2
3
4
5
6
7

addPolygon

computeMetrics

PathMetrics computeMetrics({bool forceClosed = false})

computeMetrics方法用于返回一个之前绘制的路径的一份快照。当我们使用moveTolineToarcToconicTo等绘制路径时,可以使用此来实现只绘制其中一部分。

比如:

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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

computeMetrics

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);
1
2
3
4
5
6
7
8
9

extendWithPath 图中蓝色圈起来的部分就是方法的效果

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);
1
2
3
4
5
6
7
8
9
10
11

shift

close

void close()

用于将路径起点与终点连接起来

绘制阴影-drawShadow

void drawShadow(Path path, Color color, double elevation, bool transparentOccluder)

Path绘制一个阴影

  • path 绘制阴影的路径
  • color 绘制阴影的颜色
  • elevation 绘制阴影的范围
  • transparentOccluder 是否填充path
var path = Path()
  ..moveTo(50.0, 50.0)
  ..lineTo(300.0, 50.0)
  ..lineTo(300.0, 200.0)
  ..lineTo(50.0, 200.0)
  ..close();
canvas.drawShadow(path, Colors.blue, 3, false);
// 向下平移到200 elevation设置为10
canvas.drawShadow(path.shift(Offset(0, 200)), Colors.blue, 10, false);
// 向下移动到400 transparentOccluder设置为true效果
canvas.drawShadow(path.shift(Offset(0, 400)), Colors.blue, 10, true);
1
2
3
4
5
6
7
8
9
10
11

drawShadow

绘制圆-drawCircle

void drawCircle(Offset c, double radius, Paint paint)

  • c 绘制的圆心位置
  • radius 绘制圆半径
canvas.drawCircle(Offset(200,200), 100, paint);
1

绘制一个环-drawDRRect

void drawDRRect(RRect outer, RRect inner, Paint paint)

  • outer 外部形状,由一个RRect构成(为啥是RRect,因为RRect几乎涵盖了所有的闭合形状,如矩形、正方形、圆角矩形、椭圆、圆)
  • inner 内部形状
// 画个圆角矩形
RRect rrect = RRect.fromRectXY(
    Rect.fromCircle(center: Offset(200, 200), radius: 150), 20.0, 40.0);
// 画个圆
RRect rrect1 = new RRect.fromRectXY(
    Rect.fromCircle(center: Offset(200, 200), radius: 80), 100.0, 100.0);
canvas.drawDRRect(rrect, rrect1, paint);
1
2
3
4
5
6
7

drawDRRect

总结

经过上面学习,我们知道了其实多种绘制方式能达到一样的效果

绘制点

  • canvas.drawPoints
  • path.addPolygon

绘制直线

  • drawPoints传参PointMode.polygonPointMode.lines
  • drawLine
  • path.lineTo

绘制圆弧

  • canvas.drawArc
  • path.addArc

下一篇将介绍Canvas文字的绘制及图片处理,如有兴趣,请关注

Last Updated: 6/6/2021, 5:36:46 PM