Flutter绘制系列02---画布的基础操作

本文详细介绍了如何利用Flutter的Canvas API进行图形绘制,包括平移、缩放变换,以及如何创建网格、绘制圆和线条。通过保存和恢复画布状态,实现了网格的翻转效果。同时讲解了点、线、矩形、圆角矩形、内部矩形以及圆和椭圆的绘制方法,还展示了如何应用滤镜和着色器创建特殊效果。
摘要由CSDN通过智能技术生成

画布变形和状态

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

在这里插入图片描述

注意,画布的变换是持久性的,变换之后的所有绘制都在变换后的画布上运行。
变换不是永久性的变换,需要使用状态的存储和恢复到之前画布的状态。

1.平移变换

如果想要屏幕的(0,0)点永远在屏幕的中心,可以将画布进行偏移
但是这样之后的绘制就会以中心为原点。
在这里插入图片描述

@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);
  }

2.缩放变换

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

// 通过移动画布的方式来绘制网格线
  void _drawBottomRight(Canvas canvas,Size size){
    // 保持画布状态
    canvas.save();
    // 循环绘制横向网格线
    for(int i=0;i<size.height/2/step;i++){
      // 画线,设置线的起点为0,终点为容器高度的1/2,用刚刚自定义好的画笔来画
      canvas.drawLine(Offset(0, 0), Offset(size.width/2, 0), _gridPint);
      // 设置网格往下方平移step距离
      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();
  }

在这里插入图片描述
好的,上面我们已经知道了怎么样通过canvas去画一个网格图形,并且绘制一个圆。接下来我们将会将网格铺满整个应用程序,并且在圆形外部绘制一些光线,实现效果如下:
在这里插入图片描述

@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()..color=Color(0xff0000ff));
    _drawBottomRight(canvas,size);
    canvas.save();
    canvas.scale(1,-1);
    _drawBottomRight(canvas,size);
    canvas.restore();
    canvas.save();
    canvas.scale(-1,1);
    _drawBottomRight(canvas,size);
    canvas.restore();
    canvas.save();
    canvas.scale(-1,-1);
    _drawBottomRight(canvas,size);
    canvas.restore();
  }


  // 通过移动画布的方式来绘制网格线
  void _drawBottomRight(Canvas canvas,Size size){
    _drawDot(canvas, Paint());
    // 保持画布状态
    canvas.save();
    // 循环绘制横向网格线
    for(int i=0;i<size.height/2/step;i++){
      // 画线,设置线的起点为0,终点为容器高度的1/2,用刚刚自定义好的画笔来画
      canvas.drawLine(Offset(0, 0), Offset(size.width/2, 0), _gridPint);
      // 设置网格往下方平移step距离
      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();
  }


  void _drawDot(Canvas canvas,Paint paint){
    final int count=12;
    paint..color=Colors.orangeAccent
      ..strokeWidth=5
    ..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();
  }

他的主要实现原理很简单,就是通过canvas.scale这个API来翻转画布。

/// Add an axis-aligned scale to the current transform, scaling by the first
  /// argument in the horizontal direction and the second in the vertical
  /// direction.
  ///
  /// If [sy] is unspecified, [sx] will be used for the scale in both
  /// directions.

这个是scale源码的注释,scale传递两个参数,一个做水平翻转,一个做垂直翻转。通过阅读源码,我们就可以通过scale来对网格做重复绘制

沿X轴镜像,就相当于canvas.scale(1,-1);
沿Y轴镜像,就相当于canvas.scale(-1,1);
沿原点镜像,就相当于canvas.scale(-1,-1);

基本图形绘制在这里插入图片描述

1.点绘制:drawPoints、drawRawPoints

绘制点需要传入点模式、一个Offset的列表和画笔。
使用下面的一组点进行绘点测试:

void _drawPoints(Canvas canvas){
    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),
    ];
    canvas.drawPoints(PointMode.points, points, Paint()..style=PaintingStyle.stroke..strokeWidth=10..color=Color(0xffff0000)..strokeCap=StrokeCap.round);
  }

在这里插入图片描述
好,这里点就给他画好了,下面我们来画线:
在这里插入图片描述

线的绘制其实和点的绘制一样,唯一有地方不同的就是Points的模式,PointsMode.lines如果说我们将模式变成polygon那么他就会默认你给他的标量集为边标量集,他就会开始画连线

在这里插入图片描述

void _drawPointsWithLines(Canvas canvas){
    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),
    ];
    canvas.drawPoints(PointMode.polygon, points, Paint()
      ..color=Colors.red
      ..style=PaintingStyle.stroke
      ..strokeWidth=1
      ..strokeCap=StrokeCap.round);
  }

好,以上我们完成了折线图的绘制,下面我将会想,如果说折线图画好了,我们是不是能够定义一个坐标系呢,一个简单的二维坐标系。

canvas.drawLine(Offset(-size.width/2, 0) , Offset(size.width/2, 0),_axisPaint);
canvas.drawLine(Offset( 0,-size.height/2) , Offset( 0,size.height/2),_axisPaint);

3.类矩形绘制

矩形的绘制是非常常用的操作,这里比较重要的是矩形的5种构造方法。
你可以更具不同的场景选用不同的构造方法,有时可以让计算变得更简单
下面是矩形的5种常用的构造方法,当需要构造矩形时,可以选择合适的方法进行构造。

void _drawRect(Canvas canvas){
    var rectPaint=Paint()..color=Colors.blue..strokeWidth=1.5;
    // 绘制中心矩形
    Rect rectFromCenter=Rect.fromCenter(center: Offset(0,0), width: 160, height: 160);
    canvas.drawRect(rectFromCenter, rectPaint);
    // 矩形左上右下构造
    Rect rectFromLTRB=Rect.fromLTRB(-120, -120, -80, -80);
    canvas.drawRect(rectFromLTRB, rectPaint..color=Colors.red);
    // 矩形左上宽高
    Rect rectFromLTWH=Rect.fromLTWH(70, -120, 40, 40);
    canvas.drawRect(rectFromLTWH, rectPaint..color=Colors.blue);
    // 矩形内切图构造
    Rect rectFromCircle=Rect.fromCircle(center: Offset(100,100), radius: 20);
    canvas.drawRect(rectFromCircle, rectPaint..color=Colors.black54);
    // 矩形两点构造
    Rect rectFromPoints=Rect.fromPoints(Offset(-100,-100), Offset(-80,-80));
    canvas.drawRect(rectFromPoints, rectPaint..color=Color(0xff087966));
  }

首先要说下这个矩形内切图构造,他的哪个center源码的解释是距离中心点的距离
The center argument is assumed to be an offset from the origin.
在这里插入图片描述

好了,绘制矩形没问题了,现在看绘制圆角矩形

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

void _drawRRect(Canvas canvas) {
    var radiusPaint=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), radiusPaint);

    //【2】.圆角矩形fromLTRBXY构造
    canvas.drawRRect(RRect.fromLTRBXY(-120, -120, -80, -80, 10, 10),
        radiusPaint..color = Colors.red);

    //【3】. 圆角矩形fromLTRBR构造
    canvas.drawRRect(RRect.fromLTRBR(80, -120, 120, -80, Radius.circular(10)),
        radiusPaint..color = Colors.orange);

    //【4】. 圆角矩形fromLTRBAndCorners构造
    canvas.drawRRect(
        RRect.fromLTRBAndCorners(80, 80, 120, 120,
            bottomRight: Radius.elliptical(10, 10)),
        radiusPaint..color = Colors.green);

    //【5】. 矩形两点构造
    Rect rectFromPoints = Rect.fromPoints(Offset(-120, 80), Offset(-80, 120));
    canvas.drawRRect(
        RRect.fromRectAndCorners(rectFromPoints,
            bottomLeft: Radius.elliptical(10, 10)),
        radiusPaint..color = Colors.purple);
  }

在这里插入图片描述
好的,圆角矩形我们画好了,现在我们来画内部矩形:

这个在矩形的内部做操作,本质上就是大一点的外部矩形减去后者。
后者的区域必须小于前者
在这里插入图片描述

void _drawDRRect(Canvas canvas){
    var drPaint=Paint()
        ..strokeWidth=1.5
        ..color=Colors.blue;
    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), drPaint);
  }

好的,矩形告一段落,下面开始绘制类图

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

void _drawFill(Canvas canvas){
    var fillPaint=Paint()..strokeWidth=1.5..color=Colors.green;
    canvas.save();
    canvas.translate(-200, 0);
    canvas.drawCircle(Offset(0, 0), 60, fillPaint);
    canvas.restore();
    var rect=Rect.fromCenter(center: Offset(0,0), width: 100, height: 120);
    canvas.drawOval(rect, fillPaint);
    canvas.save();
    canvas.translate(200, 0);
    canvas.drawArc(rect, 0,pi/2*3,true,fillPaint);
    canvas.restore();
  }

在这里插入图片描述

1.绘制颜色

canvas.drawColor(Colors.blue,BlendMode.lighten);

2.绘制画笔

直接使用画笔来填充画布,你可以为画笔设置滤镜或者着色器、混合模式后,进行绘制一些特效。比如下面的七彩水平渐变坐标系。
在这里插入图片描述

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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值