前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >flutter的画布认识

flutter的画布认识

作者头像
用户1974410
发布2022-09-20 16:40:41
发布2022-09-20 16:40:41
3.2K00
代码可运行
举报
文章被收录于专栏:flutter开发精选flutter开发精选
运行总次数:0
代码可运行
本节目标:

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

一、画布变换和状态

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

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

1.平移变换:

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

代码语言:javascript
代码运行次数:0
复制
---->[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(0, 0), 50, paint);
  canvas.drawLine(
      Offset(20, 20),
      Offset(50, 50),
      paint
        ..color = Colors.red
        ..strokeWidth = 5
        ..strokeCap = StrokeCap.round
        ..style = PaintingStyle.stroke);
}

2.缩放变换

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

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

代码语言:javascript
代码运行次数:0
复制
---->[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 距离,就相当于在纸上画线,你的手位置不变,而是纸在动。这样的好处是只需要做一个动作即可,比如打印机是绘制者,打印过程中打印机不会动,动的是纸。 在很多情况下,将画布进行移动可以避免很多计算过程,让绘制的逻辑更加清晰简单

代码语言:javascript
代码运行次数:0
复制
---->[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(0, 0), Offset(size.width / 2, 0), _gridPint);
    canvas.translate(0, step);
  }
  canvas.restore();
  
  canvas.save();
  for (int i = 0; i < size.width / 2 / step; i++) {
    canvas.drawLine(Offset(0, 0), Offset(0, size.height / 2), _gridPint);
    canvas.translate(step , 0);
  }
  canvas.restore();
}
代码语言:javascript
代码运行次数:0
复制
注意: 画布变换之后,如果不做处理,之后所有的操作都会在变化后画布的基础上进行。
当使用 canvas.save() 时,当前画布的状态就会被保存,当执行 canvas.restore() 时,画布就会回到上次保存的状态。
比如:在上面画横线前save画布这时画布的[顶点在屏幕中心],画横线的过程中画布的顶点被[下移到了最后]。
画完后restore画布,就能让画布顶点重新回到[屏幕中心]。

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

代码语言:javascript
代码运行次数:0
复制
---->[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(-1, 1);//沿y轴镜像
    _drawBottomRight(canvas, size);
    canvas.restore();

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

3.旋转变换:

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

代码语言:javascript
代码运行次数:0
复制
---->[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(80, 0), Offset(100, 0), paint);
    canvas.rotate(step);
  }
  canvas.restore();
}

二、基础图形绘制:

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

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

代码语言:javascript
代码运行次数:0
复制
---->[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 列表的每个点依次绘出。

PointMode.points

代码语言:javascript
代码运行次数:0
复制
void _drawPointsWithPoints(Canvas canvas) {
  _paint
    ..color = Colors.red
    ..style = PaintingStyle.stroke..strokeWidth=10
    ..strokeCap = StrokeCap.round;
  canvas.drawPoints(PointMode.points, points, _paint);
}

  • PointMode.lines : 线段模式

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

PointMode.lines

代码语言:javascript
代码运行次数:0
复制
void _drawPointsWithLines(Canvas canvas) {
  _paint
    ..color = Colors.red
    ..style = PaintingStyle.stroke
    ..strokeWidth = 1
    ..strokeCap = StrokeCap.round;
  canvas.drawPoints(PointMode.lines, points, _paint);
}

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

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

PointMode.polygon

代码语言:javascript
代码运行次数:0
复制
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 得到点数据信息,点绘制模式同上。

drawRawPoints

代码语言:javascript
代码运行次数:0
复制
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

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

drawLine

代码语言:javascript
代码运行次数:0
复制
void _drawAxis(Canvas canvas, Size size) {
  _paint..color=Colors.blue..strokeWidth=1.5;
  canvas.drawLine(Offset(-size.width/2, 0) , Offset(size.width/2, 0),_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/2, 0) , Offset(size.width/2-10, 7),_paint);
  canvas.drawLine(Offset(size.width/2, 0) , Offset(size.width/2-10, -7),_paint);
}

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

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

Screenshot_1603935956


【1】 绘制矩形 drawRect

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

代码语言:javascript
代码运行次数:0
复制
void _drawRect(Canvas canvas){
  _paint..color=Colors.blue..strokeWidth=1.5;
  //【1】.矩形中心构造
  Rect rectFromCenter = Rect.fromCenter(center: Offset(0, 0),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, -120, 40, 40);
  canvas.drawRect(rectFromLTWH, _paint..color=Colors.orange);
  //【4】. 矩形内切圆构造
  Rect rectFromCircle = Rect.fromCircle(center: Offset(100, 100),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 表示两个半轴,控制椭圆的宽扁。四个边角的圆角样式可以独立设置

代码语言:javascript
代码运行次数:0
复制
void _drawRRect(Canvas canvas) {
  _paint
    ..color = Colors.blue
    ..strokeWidth = 1.5;
  //【1】.圆角矩形fromRectXY构造
  Rect rectFromCenter =
      Rect.fromCenter(center: Offset(0, 0), width: 160, height: 160);
  canvas.drawRRect(RRect.fromRectXY(rectFromCenter, 40, 20), _paint);
  
  //【2】.圆角矩形fromLTRBXY构造
  canvas.drawRRect(RRect.fromLTRBXY(-120, -120, -80, -80, 10, 10),
      _paint..color = Colors.red);
      
  //【3】. 圆角矩形fromLTRBR构造
  canvas.drawRRect(RRect.fromLTRBR(80, -120, 120, -80, Radius.circular(10)),
      _paint..color = Colors.orange);
      
  //【4】. 圆角矩形fromLTRBAndCorners构造
  canvas.drawRRect(
      RRect.fromLTRBAndCorners(80, 80, 120, 120,
          bottomRight: Radius.elliptical(10, 10)),
      _paint..color = Colors.green);
      
  //【5】. 矩形两点构造
  Rect rectFromPoints = Rect.fromPoints(Offset(-120, 80), Offset(-80, 120));
  canvas.drawRRect(
      RRect.fromRectAndCorners(rectFromPoints,
          bottomLeft: Radius.elliptical(10, 10)),
      _paint..color = Colors.purple);
}

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

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

代码语言:javascript
代码运行次数:0
复制
后者的区域必须小于前者
代码语言:javascript
代码运行次数:0
复制
void _drawDRRect(Canvas canvas) {
  _paint
    ..color = Colors.blue
    ..strokeWidth = 1.5;
  Rect outRect =
      Rect.fromCenter(center: Offset(0, 0), width: 160, height: 160);
  Rect inRect =
      Rect.fromCenter(center: Offset(0, 0), width: 100, height: 100);
  canvas.drawDRRect(RRect.fromRectXY(outRect, 20, 20),
      RRect.fromRectXY(inRect, 20, 20), _paint);
      
  Rect outRect2 =
  Rect.fromCenter(center: Offset(0, 0), width: 60, height: 60);
  Rect inRect2 =
  Rect.fromCenter(center: Offset(0, 0), width: 40, height: 40);
  canvas.drawDRRect(RRect.fromRectXY(outRect2, 15, 15),
      RRect.fromRectXY(inRect2, 10, 10), _paint..color=Colors.green);
}

4. 绘制类圆 drawCircle,drawOval,drawArc

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

代码语言:javascript
代码运行次数:0
复制
---->[p03_canvas/06_like_circle/paper.dart]----
void _drawFill(Canvas canvas) {
    canvas.save();
    canvas.translate(-200, 0);
    canvas.drawCircle(Offset(0, 0), 60, _paint);
    canvas.restore();

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

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

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


三、其他绘制

image-20201031134411497

1. 绘制颜色 drawColor

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

原图

lighten blue

代码语言:javascript
代码运行次数:0
复制
---->[p03_canvas/07_color/paper.dart]----
canvas.drawColor(Colors.blue, BlendMode.lighten);

2.绘制画笔drawPaint

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

Screenshot_1604124045

代码语言:javascript
代码运行次数:0
复制
---->[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 / 7, 2.0 / 7, 3.0 / 7, 4.0 / 7, 5.0 / 7, 6.0 / 7, 1.0];
_paint.shader = ui.Gradient.linear(
    Offset(0, 0), Offset(size.width, 0), 
    colors, pos, TileMode.clamp);
_paint.blendMode=BlendMode.lighten;
canvas.drawPaint(_paint);

3.绘制阴影drawShadow

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

image-20201031141814533

代码语言:javascript
代码运行次数:0
复制
---->[p03_canvas/09_shadow/paper.dart]----
 Path path = Path();
 path.lineTo(80, 80);
 path.lineTo(-80, 80);
 path.close();

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

4. 绘线路径drawPath

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

image-20201031143238648

代码语言:javascript
代码运行次数:0
复制
---->[p03_canvas/10_path/paper.dart]----
Path path = Path();
path.lineTo(60, 60);
path.lineTo(-60, 60);
path.lineTo(60, -60);
path.lineTo(-60, -60);
path.close();
canvas.drawPath(path, _paint);
canvas.translate(140, 0);
canvas.drawPath(
    path,
    _paint
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2);

四、画布的裁剪:

image-20201031153932161

1、矩形裁剪:

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

Screenshot_1604126587

代码语言:javascript
代码运行次数:0
复制
---->[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 / 7, 2.0 / 7, 3.0 / 7, 4.0 / 7, 5.0 / 7, 6.0 / 7, 1.0];
_paint.shader = ui.Gradient.linear(
    rect.centerLeft, rect.centerRight,
    colors, pos, TileMode.clamp);
_paint.blendMode=BlendMode.lighten;
canvas.drawPaint(_paint);

2、圆角矩形裁剪:

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

image-20201031152953496

代码语言:javascript
代码运行次数:0
复制
---->[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

代码语言:javascript
代码运行次数:0
复制
---->[p03_canvas/13_clip_path/paper.dart]----
Path path = Path();
path.lineTo(80, 80);
path.lineTo(-80, 80);
path.close();
canvas.clipPath(path);
canvas.drawColor(Colors.red, BlendMode.darken);

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 flutter开发精选 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本节目标:
  • 一、画布变换和状态
    • 1.平移变换:
    • 2.缩放变换
    • 3.旋转变换:
  • 二、基础图形绘制:
    • 1. 点绘制 : drawPoints、drawRawPoints
    • 【1】 绘点: drawPoints
    • 【2】 绘点集: drawRawPoints
    • 2. 绘制线 : drawLine
    • 3.类矩形绘制:drawRect、drawRRect、drawDRRect
    • 【1】 绘制矩形 drawRect
    • 【2】 绘制圆角矩形 drawRRect
    • 【3】 绘制两个圆角矩形差域 drawDRRect
    • 4. 绘制类圆 drawCircle,drawOval,drawArc
  • 三、其他绘制
    • 1. 绘制颜色 drawColor
    • 2.绘制画笔drawPaint
    • 3.绘制阴影drawShadow
    • 4. 绘线路径drawPath
  • 四、画布的裁剪:
    • 1、矩形裁剪:
    • 2、圆角矩形裁剪:
    • 3、路径裁剪:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档