画布canvas

画布是一个矩形区域,我们可以控制其每一像素来绘制我们想要的内容

canvas 拥有多种绘制点、线、路径、矩形、圆形、以及添加图像的方法,结合这些方法我们可以绘制出千变万化的画面。

虽然,画布可以画这些东西,但是决定这些图形颜色、粗细表现的还是画笔。
画笔Paint

Paint非常好理解,就是我们用来画图形的工具,我们可以设置画笔的颜色、粗细、是否抗锯齿、笔触形状以及作画风格。

通过这些属性我们可以很方便的来定制自己的UI效果,当然我们在“作画”的过程中可以定义多个画笔,这样更方便我们对图形的绘制
画笔Paint的属性

canvas中有多个与绘制相关的方法,如drawLine()、drawRect()、drawOval()、drawOval()、等方法。

但是,仅仅使用canvas这个画布还不够,我们还需要一个画笔paint,我们可以使用如下代码来构建paint

Paint _paint = Paint()
    ..color = Colors.blueAccent //画笔颜色
    ..strokeCap = StrokeCap.round //画笔笔触类型
    ..isAntiAlias = true //是否启动抗锯齿
    ..blendMode = BlendMode.exclusion //颜色混合模式
    ..style = PaintingStyle.fill //绘画风格,默认为填充
    ..colorFilter = ColorFilter.mode(Colors.blueAccent,
        BlendMode.exclusion) //颜色渲染模式,一般是矩阵效果来改变的,但是flutter中只能使用颜色混合模式
    ..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) //模糊遮罩效果,flutter中只有这个
    ..filterQuality = FilterQuality.high //颜色渲染模式的质量
    ..strokeWidth = 15.0; //画笔的宽度

当然,在正常的开发中一般不会使用这么多的属性,大家可以根据需要去具体的了解和使用。
画布canvas的方法

以下内容基于此画笔绘制:

Paint _paint = new Paint()
    ..color = Colors.blueAccent
    ..strokeCap = StrokeCap.round
    ..isAntiAlias = true
    ..strokeWidth = 5.0
    ..style = PaintingStyle.stroke;

绘制直线

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

使用给定的涂料在给定点之间绘制一条线。 该行被描边,此调用忽略[Paint.style]的值。
p1和p2参数为两个点的坐标 , 在这两点之间绘制一条直线。

eg : canvas.drawLine(Offset(20.0, 20.0), Offset(100.0, 100.0), _paint)

绘制点drawPoints

void drawPoints(PointMode pointMode, List points, Paint paint)

绘制点也是非常的简单,3个参数分别为: PointMode枚举,坐标 list 和 paint
PointMode的枚举类型有三个,points(点),lines(线,隔点连接),polygon(线,相邻连接)

canvas.drawPoints(
        ///PointMode的枚举类型有三个,points(点),lines(线,隔点连接),polygon(线,相邻连接)
        PointMode.points,
        [
          Offset(20.0, 130.0),
          Offset(100.0, 210.0),
          Offset(100.0, 310.0),
          Offset(200.0, 310.0),
          Offset(200.0, 210.0),
          Offset(280.0, 130.0),
          Offset(20.0, 130.0),
        ],
        _paint..color = Colors.redAccent);

为了方便演示,我们在上面定义了7个点,第一个和最后一个点重合。

然后我们设置PointMode为points看下效果。

然后我们把PointMode改为lines

PointMode.lines

PointMode为lines时,两个点相互连接,也就是说第一个和第二个点连接,第三个跟第四个连接,如果最后只有一个点就舍弃不连接了,在我们的例子中有7个点,所以图中只有三条连线。

然后我们把PointMode改为lines

对,你看的没有错跟上面绘制线段的效果是一样的,相邻点互相连接。
绘制圆rawCircle

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

参数分别为:圆心的坐标、半径和paint即可。
圆形是否填充或描边(或两者)由Paint.style控制。

//绘制圆 参数(圆心,半径,画笔)
     canvas.drawCircle(
        Offset(100.0, 350.0),
        30.0,
        _paint
          ..color = Colors.greenAccent
          ..style = PaintingStyle.stroke //绘画风格改为stroke
        );

在这里我将画笔Paint的style改成了stroke
然后我们将画笔style改成fill (填充) ,fill也是画笔的style的默认值.

填充之后,这个圆就变成实心的了.
绘制椭圆drawOval

void drawOval(Rect rect, Paint paint)

绘制一个轴对称的椭圆形
参数为一个矩形和画笔paint.

//使用左上和右下角坐标来确定矩形的大小和位置,椭圆是在这个矩形之中内切的
    Rect rect1 = Rect.fromPoints(Offset(150.0, 200.0), Offset(300.0, 250.0));
    canvas.drawOval(rect1, _paint);

在前面我们已经讲过了使用Rect便可确认这个矩形的大小和位置。

其实,Rect也有多种构建方式:

fromPoints(Offset a, Offset b)
使用左上和右下角坐标来确定矩形的大小和位置

fromCircle({ Offset center, double radius })
使用圆的圆心点坐标和半径和确定外切矩形的大小和位置

fromLTRB(double left, double top, double right, double bottom)
使用矩形左边的X坐标、矩形顶部的Y坐标、矩形右边的X坐标、矩形底部的Y坐标来确定矩形的大小和位置

fromLTWH(double left, double top, double width, double height)
使用矩形左边的X坐标、矩形顶部的Y坐标矩形的宽高来确定矩形的大小和位置

绘制圆弧drawArc

void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)

首先还是需要Rect来确认圆弧的位置,还需要开始的弧度、结束的弧度、是否使用中心点绘制(圆弧是否向中心闭合)、以及paint.
弧度

根据定义,一周的弧度数为2πr/r=2π,360°角=2π弧度,因此,1弧度约为57.3°,即57°17’44.806’’,1°为π/180弧度,近似值为0.01745弧度,周角为2π弧度,平角(即180°角)为π弧度,直角为π/2弧度。
特殊的弧度:
度     弧度
0°     0
30°     π/6
45°     π/4
60°     π/3
90°     π/2
120°     2π/3
180°     π
270°     3π/2
360°     2π

//绘制圆弧
    // Rect来确认圆弧的位置,还需要开始的弧度、结束的弧度、是否使用中心点绘制、以及paint弧度
    Rect rect2 = Rect.fromCircle(center: Offset(200.0, 50.0), radius: 80.0);
    canvas.drawArc(rect2, 0.0, 0.8, false, _paint);

在这里插入图片描述
绘制个90度的弧度

const PI = 3.1415926;
    Rect rect2 = Rect.fromCircle(center: Offset(200.0, 50.0), radius: 80.0);
    canvas.drawArc(rect2, 0.0, PI / 2, false, _paint);

定义π为3.1415926,定义开始的角度为0°扫过的角度为PI / 2(90°),设置userCenter为false

将useCenter改成true 试试:

发现圆弧向中心点闭合了.
绘制圆角矩形drawDRRect

void drawRRect(RRect rrect, Paint paint)

使用RRect确定矩形大小及弧度,使用paint来完成绘制。

RRect构建起来也非常的方便,直接使用fromRectAndRadius即可

RRect.fromRectAndRadius(rect, radius)

rect依然用来表示矩形的位置和大小,radius用来表示圆角的大小。

//用Rect构建一个边长50,中心点坐标为100,100的矩形
    Rect rect = Rect.fromCircle(center: Offset(100.0, 150.0), radius: 50.0);
    //根据上面的矩形,构建一个圆角矩形
    RRect rrect = RRect.fromRectAndRadius(rect, Radius.circular(20.0));
    canvas.drawRRect(rrect, _paint);

将圆角的半径设置为边长(从20改成50)试一下:

就变成了圆.
绘制双圆角矩形drawRRect

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

和drawRRect类似,使用RRect确定内部、外部矩形大小及弧度,使用paint来完成绘制。

//绘制两个矩形
    Rect rect1 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 60.0);
    Rect rect2 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 40.0);

//分别绘制外部圆角矩形和内部的圆角矩形
    RRect outer = RRect.fromRectAndRadius(rect1, Radius.circular(10.0));
    RRect inner = RRect.fromRectAndRadius(rect2, Radius.circular(10.0));
    canvas.drawDRRect(outer, inner, _paint);

使用Rect.fromCircle来创建Rect,使用RRect.fromRectAndRadius来创建RRect

可以看到两个圆角矩形,当然我们也可以尝试调整角度的度数大小。

//绘制两个矩形
    Rect rect1 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 60.0);
    Rect rect2 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 40.0);

//分别绘制外部圆角矩形和内部的圆角矩形
    RRect outer = RRect.fromRectAndRadius(rect1, Radius.circular(30.0));
    RRect inner = RRect.fromRectAndRadius(rect2, Radius.circular(5.0));
    canvas.drawDRRect(outer, inner, _paint);

你甚至可以调整角度的大小使两个矩形都变成圆来形成一个圆环.
绘制路径drawPath

void drawPath(Path path, Paint paint)

绘制路径,首先需要一个要绘制的路径path,然后就是这个paint了。
Path的常用方法:
方法名     作用
moveTo     将路径起始点移动到指定的位置
relativeMoveTo     相对于当前位置移动到
lineTo     从当前位置连接指定点
relativeLineTo     相对当前位置连接到
arcTo     曲线
conicTo     贝塞尔曲线
add**     添加其他图形,如addArc,在路径是添加圆弧
contains     路径上是否包括某点
transfor     给路径做matrix4变换
combine     结合两个路径
close     关闭路径,连接路径的起始点
reset     重置路径,恢复到默认状态

eg:

//新建了一个path,然后将路径起始点移动到坐标(100,100)的位置
    Path path = new Path()..moveTo(100.0, 100.0);

path.lineTo(200.0, 200.0);

canvas.drawPath(path, _paint);

首先新建了一个path,然后将路径起始点移动到坐标(100,100)的位置,
然后从这个位置连接(200,200)的点.

我们也可以绘制多个路径:

Path path = new Path()..moveTo(100.0, 100.0);

path.lineTo(200.0, 200.0);
    path.lineTo(100.0, 300.0);
    path.lineTo(150.0, 350.0);
    path.lineTo(150.0, 500.0);
    
    canvas.drawPath(path, _paint);

使用二阶贝塞尔曲线绘制弧线:

void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo)

rect我们都知道了,是一个矩形,startAngle是开始的弧度,sweepAngle是结束的弧度
重点介绍一下forceMoveTo.
forceMoveTo:

如果“forceMoveTo”参数为false,则添加一条直线段和一条弧段。
    如果“forceMoveTo”参数为true,则启动一个新的子路径,其中包含一个弧段。

例如:

Path path = new Path()..moveTo(100.0, 100.0);
    Rect rect = Rect.fromCircle(center: Offset(200.0, 200.0), radius: 60.0);
    path.arcTo(rect, 0.0, 3.14, false);
    canvas.drawPath(path, _paint);

这里,我们利用贝塞尔曲线绘制了一个半圆,因为起始点的坐标是(100,100),而我们绘制贝塞尔曲线的时候,曲线的原点是(200,200)半径,60,所以我们移动到(200,260)的位置再画这个曲线.

因为forceMoveTo此时为false,所以从起始点到曲线的起始点画出了直线路径,
改为true可以看到,因为启动了一个新的子路径,所以那条线段没有了:

当然,你甚至可以用贝塞尔曲线直接画一个圆:

Rect rect = Rect.fromCircle(center: Offset(200.0, 200.0), radius: 60.0);

path.arcTo(rect, 0.0, 3.14*2, false);

canvas.drawPath(path, _paint);

使用三阶贝塞尔曲线绘制❤:

void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3)

var width = 200;
    var height = 300;
    path.moveTo(width / 2, height / 4);
    path.cubicTo((width * 6) / 7, height / 9, (width * 13) / 13,
        (height * 2) / 5, width / 2, (height * 7) / 12);
    canvas.drawPath(path, _paint);

Path path2 = new Path();
    path2.moveTo(width / 2, height / 4);
    path2.cubicTo(width / 7, height / 9, width / 21, (height * 2) / 5,
        width / 2, (height * 7) / 12);
    canvas.drawPath(path2, _paint);

看一下效果:

然后我们改变paint的样式:

canvas.drawPath(path, _paint);
 替换为:
 canvas.drawPath(
        path,
        _paint
          ..style = PaintingStyle.fill
          ..color = Colors.red);

我们将画笔的颜色改成红色,样式改为填充:

绘制颜色drawColor

void drawColor(Color color, BlendMode blendMode)

我们先绘制一个圆:

canvas.drawCircle(Offset(100.0, 100.0), 50.0, _paint);

然后我们添加一行代码:

canvas.drawCircle(Offset(100.0, 100.0), 50.0, _paint);
    canvas.drawColor(Colors.red, BlendMode.color);  // 添加这行

可以看到,圆的颜色变成了红色,
我们还可以改变BlendMode, 例如:BlendMode.colorDodge

更多效果可以查询BlendMode源码.
绘制图片drawImage

void drawImage(Image image, Offset p, Paint paint)

将给定的[image]以其左上角的[偏移量]绘制到画布中
首先我们需要获取本地图片文件,然后绘制图片即可
绘制一个圆形的进度条

综合所学,实现一个进度条组件

全文相关代码已提交到 github
---------------------  
作者:__卓原  
来源:CSDN  
原文:https://blog.csdn.net/u011272795/article/details/83828732  
版权声明:本文为博主原创文章,转载请附上博文链接!

flutter自定义View(CustomPainter) 之 canvas的方法总结的更多相关文章

  1. Android 自定义 view(四)—— onMeasure 方法理解

    前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...

  2. Android绘图机制(一)——自定义View的基础属性和方法

    Android绘图机制(一)--自定义View的基础属性和方法 自定义View看起来,确实看起来高深莫测,很多Android开发都不是特别在行这一块,这里面的逻辑以及一些绘画都是有一点难的,说一下我目 ...

  3. Android 自定义 view(三)—— onDraw 方法理解

    前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...

  4. 【Android 界面效果46】自定义view常处理的回调方法

    onFinishInflate() 当View中所有的子控件均被映射成xml后触发 onMeasure(int, int) 确定所有子元素的大小 onLayout(boolean, int, int, ...

  5. Android自定义View的实现方法,带你一步步深入了解View(四)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不知不觉中,带你一步步深入了解View系列的文章已经写到第四篇了,回 ...

  6. View (五)自定义View的实现方法

    一些接触Android不久的朋友对自定义View都有一丝畏惧感,总感觉这是一个比较高级的技术,但其实自定义View并不复杂,有时候只需要简单几行代码就可以完成了. 如果说要按类型来划分的话,自定义Vi ...

  7. 【转】Android自定义View的实现方法,带你一步步深入了解View(四)

    原文网址: 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不知不觉中,带你一步步深入了解View系列的文章已经写到 ...

  8. 自定义View(二),强大的Canvas

    本文转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android中使用图形处理引擎,2D部分是 ...

  9. Android -- 自定义View小Demo,关于Path类的使用(一)

    1,在我们知道自定义view中onDraw()方法是用于绘制图形的,而Path类则是其中的一个重要的类,如下图效果: 代码也没有什么难度,直接贴出来吧 @Override protected void ...

随机推荐

  1. IDOC 实例测试

    这份文档主要是自己学习IDOC的一些练习过程及心得,可能讲的不全面,但应该可以帮助大家了解IDOC的一些工作方式. IDOC或者说是ALE,事实上,是SAP用于分布和集成数据的一种方式.所以,我个人就 ...

  2. 跟我学Makefile(一)

    1.首先,把源文件编译生成中间代码文件,Windows下.obj文件,unix下.o文件,即Object File.这个动作叫编译(compile) 把大量的Object File合并执行文件,叫做链 ...

  3. 子集和问题(应用--换零钱)POJ2229:Sumsets

    我一直在纠结换零钱这一类型的题目,今天好好絮叨一下,可以说他是背包的应用,也可以说他是单纯的dp.暂且称他为dp吧. 先上一道模板题目. sdut2777: 小P的故事——神奇的换零钱 题目描述 已知 ...

  4. hash 冲突及解决办法。

    hash 冲突及解决办法. 关键字值不同的元素可能会映象到哈希表的同一地址上就会发生哈希冲突.解决办法: 1)开放定址法:当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列.沿 ...

  5. 在vue中使用express-mock搭建mock服务

    首先安装 nodemon ,如果是全局安装,那么所有的项目都可以使用mock服务 npm install nodemon 再安装express-mockjs npm i -D express-mock ...

  6. java中全面的单例模式多种实现方式总结

    单例模式的思想 想整理一些 java 并发相关的知识,不知道从哪开始,想起了单例模式中要考虑的线程安全,就从单例模式开始吧. 以前写过单例模式,这里再重新汇总补充整理一下,单例模式的多种实现. 单例模 ...

  7. 自己写个 Drools 文件语法检查工具——栈的应用之编译器检测语法错误

    一.背景 当前自己开发的 Android 项目是一个智能推荐系统,用到 drools 规则引擎,于我来说是一个新知识点,以前都没听说过的东东,不过用起来也不算太难,经过一段时间学习,基本掌握.关于 d ...

  8. IDEA 程序直接运行分析

    今天用IDEA运行SpringBoot程序,启动时始终报错说读取不到datasource的url配置. 分析代码的resources目录,是有配置文件的,配置也是正常的.如下图: 后来经人指点,是因为 ...

  9. 转载:逻辑回归的python实现

    转载自:http://blog.csdn.net/zouxy09/article/details/20319673 一.逻辑回归(LogisticRegression) Logistic regres ...

  10. Unix/Linux操作系统中如何在sqlplus/rman中使用方向键

    默认情况下在Unix/Linux中使用Oracle的sqlplus/rman是无法使用↑↓←→几个方向键进行操作的,要想达到Windows下使用sqlplus/rman的效果需要安装rlwrap. r ...