flutter的画布认识

Posted flutter开发精选

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flutter的画布认识相关的知识,希望对你有一定的参考价值。


本节目标:

[1]. 认识画布的变换和状态 (save/restore)。
[2]. 基础图形的绘制操作:绘制点、绘制线、绘制类矩形、绘制类圆。
[3]. 其他绘制:绘制颜色、绘制画笔、绘制阴影、绘制路径。
[4]. 画布裁剪:矩形裁剪、圆角矩形裁剪、路径裁剪。

一、画布变换和状态

画布变换主要通过一个 4*4 的变换矩阵。其中transform方法是最核心的,也是最难用的。
不过另外四个方法是为了简便使用,对 transform 的封装。

注意: 画布的变换是持久性的,变换之后所有的绘制会在变换后的画布上进行。
变换不是永久性的变换,需要使用状态的存储【save】和恢复【restore】回到之前的画布状态。

1.平移变换:

如果想要屏幕的 (0,0) 点永久在屏幕中心,可以将画布进行偏移
这样之后的绘制就会以中心为原点。

flutter的画布认识
---->[p03_canvas/01_operation_translate/paper.dart]----
@override
void paint(Canvas canvas, Size size) {
  var paint = Paint()
    ..style = PaintingStyle.fill
    ..color = Colors.blue;
  // 画布起点移到屏幕中心
  canvas.translate(size.width / 2, size.height / 2);
  canvas.drawCircle(Offset(00), 50, paint);
  canvas.drawLine(
      Offset(2020),
      Offset(5050),
      paint
        ..color = Colors.red
        ..strokeWidth = 5
        ..strokeCap = StrokeCap.round
        ..style = PaintingStyle.stroke);
}

2.缩放变换

【目标】: 现在通过变换实现一个圆点在中心的网格

[1]. 练习平移操作: 通过线的平移绘制出右下角四分之一网格线
[2]. 练习缩放操作: 通过缩放四分之一网格线,绘制出另外四分之三网格线
[3]. 了解画布的存储【save】和恢复【restore】用法

---->[p03_canvas/02_operation_scale_grid/paper.dart]----  
class PaperPainter extends CustomPainter {
  Paint _gridPint; // 画笔
  final double step = 20// 小格边长
  final double strokeWidth = .5// 线宽
  final Color color = Colors.grey; // 线颜色

  PaperPainter() {
    _gridPint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..color = color;
  }

实现方式是画一条直线,然后通过画板的平移,进行画线。
如下代码中,绘制横线时使用的点位是都是 Offset(0, 0), Offset(size.width / 2, 0)
只是在每次画完后,将画布向下移 step 距离,就相当于在纸上画线,你的手位置不变,而是纸在动。这样的好处是只需要做一个动作即可,比如打印机是绘制者,打印过程中打印机不会动,动的是纸。
在很多情况下,将画布进行移动可以避免很多计算过程,让绘制的逻辑更加清晰简单

flutter的画布认识
---->[p03_canvas/02_operation_scale_grid/paper.dart]---- 
void _drawBottomRight(Canvas canvas, Size size) {
  canvas.save();
  for (int i = 0; i < size.height / 2 / step; i++) {
    canvas.drawLine(Offset(00), Offset(size.width / 20), _gridPint);
    canvas.translate(0, step);
  }
  canvas.restore();
  
  canvas.save();
  for (int i = 0; i < size.width / 2 / step; i++) {
    canvas.drawLine(Offset(00), Offset(0, size.height / 2), _gridPint);
    canvas.translate(step , 0);
  }
  canvas.restore();
}
注意: 画布变换之后,如果不做处理,之后所有的操作都会在变化后画布的基础上进行。
当使用 canvas.save() 时,当前画布的状态就会被保存,当执行 canvas.restore() 时,画布就会回到上次保存的状态。
比如:在上面画横线前save画布这时画布的[顶点在屏幕中心],画横线的过程中画布的顶点被[下移到了最后]。
画完后restore画布,就能让画布顶点重新回到[屏幕中心]。

现在已经画完四分之一了,也许你正想傻傻再画出其他三个。有更巧妙的方法:
如果是相同或者对称的对象,可以通过缩放进行对称变化。
沿x轴镜像,就相当于canvas.scale(1, \-1)
沿y轴镜像,就相当于canvas.scale(-1, 1)
沿原点镜像,就相当于canvas.scale(-1, \-1)

flutter的画布认识
---->[p03_canvas/02_operation_scale_grid/paper.dart]---- 
void _drawGrid(Canvas canvas, Size size) {
    _drawBottomRight(canvas, size);
    canvas.save();
    canvas.scale(1-1);//沿x轴镜像
    _drawBottomRight(canvas, size);
    canvas.restore();

    canvas.save();
    canvas.scale(-11);//沿y轴镜像
    _drawBottomRight(canvas, size);
    canvas.restore();

    canvas.save();
    canvas.scale(-1-1);//沿原点镜像
    _drawBottomRight(canvas, size);
    canvas.restore();
  }

3.旋转变换:

如下,通过旋转画布可以画出一圈的小线,不过不要画布旋转,而是计算点位,那就会相当痛苦。
遍历 12 次,每次将画布旋转2 * pi / count弧度,这样就可以绘制一圈的小线。

flutter的画布认识
---->[p03_canvas/03_operation_rotate/paper.dart]---- 
void _drawDot(Canvas canvas, Paint paint) {
  final int count = 12;
  paint
    ..color = Colors.orangeAccent
    ..style = PaintingStyle.stroke;
  canvas.save();
  for (int i = 0; i < count; i++) {
    var step = 2 * pi / count;
    canvas.drawLine(Offset(800), Offset(1000), paint);
    canvas.rotate(step);
  }
  canvas.restore();
}

二、基础图形绘制:

flutter的画布认识

1. 点绘制 : drawPoints、drawRawPoints
【1】 绘点: drawPoints

绘制点需要传入点模式pointMode、一个 Offset 的列表和画笔。
使用下面的一组点进行绘点测试:本节源码p03_canvas/04_point_line/paper.dart

---->[p03_canvas/04_point_line/paper.dart]----
final List<Offset> points = [
  Offset(-120-20),
  Offset(-80-80),
  Offset(-40-40),
  Offset(0-100),
  Offset(40-140),
  Offset(80-160),
  Offset(120-100),
];
  • PointMode.points : 点模式

点模式下就是将 Offset 列表的每个点依次绘出。

flutter的画布认识
PointMode.points
void _drawPointsWithPoints(Canvas canvas) {
  _paint
    ..color = Colors.red
    ..style = PaintingStyle.stroke..strokeWidth=10
    ..strokeCap = StrokeCap.round;
  canvas.drawPoints(PointMode.points, points, _paint);
}

  • PointMode.lines : 线段模式

线段模式下:每两个点一对形成线段。如果点是奇数个,那么最后一个点将没有用。

flutter的画布认识
PointMode.lines
void _drawPointsWithLines(Canvas canvas) {
  _paint
    ..color = Colors.red
    ..style = PaintingStyle.stroke
    ..strokeWidth = 1
    ..strokeCap = StrokeCap.round;
  canvas.drawPoints(PointMode.lines, points, _paint);
}

  • PointMode.polygon : 多边形连线模式

多边形连线模式下:所有的点依次连接成图形。

flutter的画布认识
PointMode.polygon
void _drawPointLineWithPolygon(Canvas canvas) {
  _paint
    ..color = Colors.red
    ..style = PaintingStyle.stroke
    ..strokeWidth = 1
    ..strokeCap = StrokeCap.round;
  canvas.drawPoints(PointMode.polygon, points, _paint);
}

【2】 绘点集: drawRawPoints

通过 Float32List 得到点数据信息,点绘制模式同上。

flutter的画布认识
drawRawPoints
void _drawRawPoints(Canvas canvas) {
  Float32List pos = Float32List.fromList([
    -120-20,-80-80,-40,
    -40,0-100,40-140,
    80-160,120-100]);
  _paint
    ..color = Colors.red
    ..style = PaintingStyle.stroke
    ..strokeWidth = 10
    ..strokeCap = StrokeCap.round;
  canvas.drawRawPoints(PointMode.points, pos, _paint);
}

2. 绘制线 : drawLine

指定两点绘制一条线,如下的两个蓝色坐标轴由六条线构成(包括两个尖角的线)。
将上面的点绘制效果保留,会呈现如下的折线图:

flutter的画布认识
drawLine
void _drawAxis(Canvas canvas, Size size) {
  _paint..color=Colors.blue..strokeWidth=1.5;
  canvas.drawLine(Offset(-size.width/20) , Offset(size.width/20),_paint);
  canvas.drawLine(Offset( 0,-size.height/2) , Offset( 0,size.height/2),_paint);
  canvas.drawLine(Offset( 0,size.height/2) , Offset( 0-7.0,size.height/2-10),_paint);
  canvas.drawLine(Offset( 0,size.height/2) , Offset( 0+7.0,size.height/2-10),_paint);
  canvas.drawLine(Offset(size.width/20) , Offset(size.width/2-107),_paint);
  canvas.drawLine(Offset(size.width/20) , Offset(size.width/2-10-7),_paint);
}

3.类矩形绘制:drawRect、drawRRect、drawDRRect

矩形的绘制是非常常用的操作,这里比较重要的是矩形的五种构造方法
你可以根据不同的场景选用不同的构造方法,有时可以让计算变的简单。
下面是本节要绘制的内容,源码位置:p03_canvas/05_like_rect/paper.dart

flutter的画布认识
Screenshot_1603935956

【1】 绘制矩形 drawRect

下面是矩形的五种构造方法,当需要构造矩形时,可以选择合适的方法方便构造。

flutter的画布认识
void _drawRect(Canvas canvas){
  _paint..color=Colors.blue..strokeWidth=1.5;
  //【1】.矩形中心构造
  Rect rectFromCenter = Rect.fromCenter(center: Offset(00),width: 160,height: 160);
  canvas.drawRect(rectFromCenter, _paint);
  //【2】.矩形左上右下构造
  Rect rectFromLTRB = Rect.fromLTRB(-120-120-80-80);
  canvas.drawRect(rectFromLTRB, _paint..color=Colors.red);
  //【3】. 矩形左上宽高构造
  Rect rectFromLTWH = Rect.fromLTWH(80-1204040);
  canvas.drawRect(rectFromLTWH, _paint..color=Colors.orange);
  //【4】. 矩形内切圆构造
  Rect rectFromCircle = Rect.fromCircle(center: Offset(100100),radius: 20);
  canvas.drawRect(rectFromCircle, _paint..color=Colors.green);
  //【5】. 矩形两点构造
  Rect rectFromPoints= Rect.fromPoints(Offset(-120 , 80),Offset(-80 , 120));
  canvas.drawRect(rectFromPoints, _paint..color=Colors.purple);
}

【2】 绘制圆角矩形 drawRRect

圆角矩形可以通过一个矩形域 Rect 和一个圆角对象 Radius 构成
6 个构造方法因地制宜,圆角是一个四分之一椭圆,其中 x,y 表示两个半轴,控制椭圆的宽扁。四个边角的圆角样式可以独立设置

flutter的画布认识
void _drawRRect(Canvas canvas) {
  _paint
    ..color = Colors.blue
    ..strokeWidth = 1.5;
  //【1】.圆角矩形fromRectXY构造
  Rect rectFromCenter =
      Rect.fromCenter(center: Offset(00), width: 160, height: 160);
  canvas.drawRRect(RRect.fromRectXY(rectFromCenter, 4020), _paint);
  
  //【2】.圆角矩形fromLTRBXY构造
  canvas.drawRRect(RRect.fromLTRBXY(-120-120-80-801010),
      _paint..color = Colors.red);
      
  //【3】. 圆角矩形fromLTRBR构造
  canvas.drawRRect(RRect.fromLTRBR(80-120120-80, Radius.circular(10)),
      _paint..color = Colors.orange);
      
  //【4】. 圆角矩形fromLTRBAndCorners构造
  canvas.drawRRect(
      RRect.fromLTRBAndCorners(8080120120,
          bottomRight: Radius.elliptical(1010)),
      _paint..color = Colors.green);
      
  //【5】. 矩形两点构造
  Rect rectFromPoints = Rect.fromPoints(Offset(-12080), Offset(-80120));
  canvas.drawRRect(
      RRect.fromRectAndCorners(rectFromPoints,
          bottomLeft: Radius.elliptical(1010)),
      _paint..color = Colors.purple);
}

【3】 绘制两个圆角矩形差域 drawDRRect

核心是找到两个圆角矩形的区域,前者减去后者。

后者的区域必须小于前者
flutter的画布认识
void _drawDRRect(Canvas canvas) {
  _paint
    ..color = Colors.blue
    ..strokeWidth = 1.5;
  Rect outRect =
      Rect.fromCenter(center: Offset(00), width: 160, height: 160);
  Rect inRect =
      Rect.fromCenter(center: Offset(00), width: 100, height: 100);
  canvas.drawDRRect(RRect.fromRectXY(outRect, 2020),
      RRect.fromRectXY(inRect, 2020), _paint);
      
  Rect outRect2 =
  Rect.fromCenter(center: Offset(00), width: 60, height: 60);
  Rect inRect2 =
  Rect.fromCenter(center: Offset(00), width: 40, height: 40);
  canvas.drawDRRect(RRect.fromRectXY(outRect2, 1515),
      RRect.fromRectXY(inRect2, 1010), _paint..color=Colors.green);
}

4. 绘制类圆 drawCircle,drawOval,drawArc

类圆主要有圆、椭圆、圆弧,圆是一个中心点 Offset 和半径组成,椭圆的形状由一个矩形域确定。

flutter的画布认识
---->[p03_canvas/06_like_circle/paper.dart]----
void _drawFill(Canvas canvas) {
    canvas.save();
    canvas.translate(-2000);
    canvas.drawCircle(Offset(00), 60, _paint);
    canvas.restore();

    var rect = Rect.fromCenter(center: Offset(00), height: 100, width: 120);
    canvas.drawOval(rect, _paint);

    canvas.save();
    canvas.translate(2000);
    //drawArc(矩形区域,起始弧度,扫描弧度,是否连中心,画笔)
    canvas.drawArc(rect, 0, pi / 2 * 3true, _paint);
    canvas.restore();
  }
drawArc 的第四参,表示是否使用中心点,下左图为 false,表示两端不与中间连线。
中间图为 true,表示两端与中间连线。

下图代码详见: p03_canvas/06_like_circle/paper.dart#_drawArcDetail

flutter的画布认识

三、其他绘制

flutter的画布认识
image-20201031134411497
1. 绘制颜色 drawColor

左侧是原图,在此基础上绘制颜色,需要传入颜色混合模式
如下使用蓝色的 BlendMode.lighten,结果为右图。注: 混合模式将在[Color篇]详解

原图 lighten blue
flutter的画布认识 flutter的画布认识
---->[p03_canvas/07_color/paper.dart]----
canvas.drawColor(Colors.blue, BlendMode.lighten);

2.绘制画笔drawPaint

直接使用画笔填充画布。你可以为画笔设置滤镜着色器混色模式后,进行绘制一些特效。
比如下面的七彩水平渐变坐标系

flutter的画布认识
Screenshot_1604124045
---->[p03_canvas/08_paint/paper.dart]----
var colors = [
  Color(0xFFF60C0C),
  Color(0xFFF3B913),
  Color(0xFFE7F716),
  Color(0xFF3DF30B),
  Color(0xFF0DF6EF),
  Color(0xFF0829FB),
  Color(0xFFB709F4),
];
var pos = [1.0 / 72.0 / 73.0 / 74.0 / 75.0 / 76.0 / 71.0];
_paint.shader = ui.Gradient.linear(
    Offset(00), Offset(size.width, 0), 
    colors, pos, TileMode.clamp);
_paint.blendMode=BlendMode.lighten;
canvas.drawPaint(_paint);

3.绘制阴影drawShadow

阴影是根据路径绘制的,drawShadow 有四个参数:
路径 path颜色 color影深 elevation内部是否显示 transparentOccluder

flutter的画布认识
image-20201031141814533
---->[p03_canvas/09_shadow/paper.dart]----
 Path path = Path();
 path.lineTo(8080);
 path.lineTo(-8080);
 path.close();

 canvas.drawShadow(path, Colors.red, 3true);
 canvas.translate(2000);
 canvas.drawShadow(path, Colors.red, 3false);

4. 绘线路径drawPath

drawPath是一个极其重要的 API,为绘制提供了无限可能。主要用于将一个路径绘制出来。

flutter的画布认识
image-20201031143238648
---->[p03_canvas/10_path/paper.dart]----
Path path = Path();
path.lineTo(6060);
path.lineTo(-6060);
path.lineTo(60-60);
path.lineTo(-60-60);
path.close();
canvas.drawPath(path, _paint);
canvas.translate(1400);
canvas.drawPath(
    path,
    _paint
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2);

四、画布的裁剪:

flutter的画布认识
image-20201031153932161
1、矩形裁剪:

指定一个矩形,画布在之后的绘制中仅保留矩形内的内容 。可通过save/restore存换状态。
下面是在裁剪后进行渐变色的绘制,可见,只在矩形域内生效。另外有两个可选属性:
doAntiAlias: 是否抗锯齿 默认true
clipOp: ClipOp.intersect 裁内部(默认) ClipOp.difference 裁外部

flutter的画布认识
Screenshot_1604126587
---->[p03_canvas/11_clip_rect/paper.dart]----
var rect = Rect.fromCenter(center: Offset.zero,width: 360,height: 240);
canvas.clipRect(rect,doAntiAlias: true,clipOp: ui.ClipOp.intersect); // 裁剪画布
var colors = [
  Color(0xFFF60C0C),
  Color(0xFFF3B913),
  Color(0xFFE7F716),
  Color(0xFF3DF30B),
  Color(0xFF0DF6EF),
  Color(0xFF0829FB),
  Color(0xFFB709F4),
];
var pos = [1.0 / 72.0 / 73.0 / 74.0 / 75.0 / 76.0 / 71.0];
_paint.shader = ui.Gradient.linear(
    rect.centerLeft, rect.centerRight,
    colors, pos, TileMode.clamp);
_paint.blendMode=BlendMode.lighten;
canvas.drawPaint(_paint);

2、圆角矩形裁剪:

和矩形裁剪基本一致,只不过效果作用于:一个圆角矩形区域

image-20201031152953496
---->[p03_canvas/12_clip_rrect/paper.dart]----
var rect = Rect.fromCenter(center: Offset.zero,width: 200,height: 100);
canvas.clipRRect(RRect.fromRectAndRadius(rect, Radius.circular(30)));

canvas.drawColor(Colors.red, BlendMode.darken);

3、路径裁剪:

和矩形裁剪基本一致,只不过效果作用于:一个指定路径的区域
下面使用一个三角形的路径裁剪画布,drawColor 就会只作用于当前区域内

image-20201031152727502
---->[p03_canvas/13_clip_path/paper.dart]----
Path path = Path();
path.lineTo(8080);
path.lineTo(-8080);
path.close();
canvas.clipPath(path);
canvas.drawColor(Colors.red, BlendMode.darken);

现在我们已经对画布的基本操作和基础形状绘制了解完毕,接下来看一下画布对图像文字的绘制支持。

以上是关于flutter的画布认识的主要内容,如果未能解决你的问题,请参考以下文章

在画布上绘制片段视图

错误记录Flutter 混合开发获取 BinaryMessenger 报错 ( FlutterActivityAndFragmentDelegate.getFlutterEngine() )(代码片段

javascript 有用的片段关于画布

在 webview_flutter 中启用捏合和缩放,在哪里添加代码片段 [this.webView.getSettings().setBuiltInZoomControls(true);]

如何在 Flutter 中实现可滚动的画布?

如何在 Flutter 的画布上的路径内绘制图案?