Flutter 35: 图解自定义 View 之 Canvas

作者: 阿策神奇 | 来源:发表于2019-03-04 20:39 被阅读20次

          小菜前几天整理了以下 Canvas 的部分方法,今天小菜继续学习 Canvas 第二部分。

    drawXXX

    drawShadow 绘制阴影

          drawShadow 用于绘制阴影,第一个参数时绘制一个图形 Path,第二个是设置阴影颜色,第三个为阴影范围,最后一个阴影范围是否填充满;

    canvas.drawShadow(
        Path()
          ..moveTo(30.0, 30.0)..lineTo(120.0, 30.0)
          ..lineTo(120.0, 60.0)..lineTo(30.0, 60.0)
          ..close(),
        Colors.blue, 3, false);
    canvas.drawShadow(
        Path()
          ..moveTo(30.0, 90.0)..lineTo(120.0, 90.0)
          ..lineTo(120.0, 120.0)..lineTo(30.0, 120.0),
        Colors.blue, 10, false);
    canvas.drawShadow(
        Path()
          ..moveTo(30.0, 150.0)..lineTo(120.0, 150.0)
          ..lineTo(120.0, 180.0)..lineTo(30.0, 180.0)
          ..close(),
        Colors.blue, 3, true);
    canvas.drawPath(
        Path()
          ..moveTo(30.0, 210.0)..lineTo(120.0, 210.0)
          ..lineTo(120.0, 240.0)..lineTo(30.0, 240.0),
        Paint()..color = Colors.blue..strokeWidth = 2..style = PaintingStyle.stroke);
    
    drawImage 绘制图片

          drawImage 用于绘制图片,绘制图片是重点,此时的 Image 并非日常所用的图片加载,而是用的 dart.ui 类中的 ui.Image 并转换成字节流 ImageStream 方式传递,包括本地图片或网络图片

    // 获取图片 本地为false 网络为true
    Future<ui.Image> _loadImage(var path, bool isUrl) async {
      ImageStream stream;
      if (isUrl) {
        stream = NetworkImage(path).resolve(ImageConfiguration.empty);
      } else {
        stream =
            AssetImage(path, bundle: rootBundle).resolve(ImageConfiguration.empty);
      }
      Completer<ui.Image> completer = Completer<ui.Image>();
      void listener(ImageInfo frame, bool synchronousCall) {
        final ui.Image image = frame.image;
        completer.complete(image);
        stream.removeListener(listener);
      }
    
      stream.addListener(listener);
      return completer.future;
    }
    
    // 加载图片
    _prepareImg() {
      _loadImage('images/icon_hzw02.jpg', false).then((image1) {
        _image1 = image1;
      }).whenComplete(() {
        _loadImage('https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=703702342,1604162245&fm=26&gp=0.jpg', true)
            .then((image2) {
          _image2 = image2;
        }).whenComplete(() {
          _prepDone = true;
          if (this.mounted) {
            setState(() {});
          }
        });
      });
    }
    
    canvas.drawImage(this.image, ui.Offset(120.0, 540.0), Paint());
    canvas.drawImage(this.image2, ui.Offset(60.0, 60.0), Paint());
    

    Tips:

          小菜在尝试过程中总是加载失败,后来理解为绘制过程需要时间,可以通过 setState(() {});whenComplete 判断状态后进行刷新,这种方式并非最佳,希望有更好方式的朋友多指导。

    drawImageRect 绘制矩形图片

          drawImageRect 除了可以正常绘制图片之外,还可以绘制图片部分内容,如下:第一个参数为 ui.Image,第二个参数为需要原图绘制矩形范围,第三个参数为本次绘制矩形范围,最后一个为画笔;

          小菜绘制原图与部分图进行对比,drawImageRect 绘制的原图粉色圈出的范围;且 drawImageRect 效率更好,推荐使用;

    canvas.drawImage(this.image, ui.Offset(60.0, 540.0), Paint());
    canvas.drawImageRect(
        this.image,
        Rect.fromLTWH(0, 0, 60, 60),
        Rect.fromLTWH(60, 60, image.width.toDouble(), image.height.toDouble()),
        Paint());
    
    drawImageNine 绘制九图

          drawImageNine 同样用来绘制图片,与原图绘制方式不同在于,drawImageNine 中第二个参数矩形变长延伸将原图分割为九部分,划为中心区域,第三个参数矩形即绘制整体矩形范围,包括四个顶点位置;小菜绘制原图与部分图进行对比,drawImageNine 绘制的原图绿色圈出的范围,小菜感觉类似于裁剪了原图;

    canvas.drawImage(this.image, ui.Offset(60.0, 540.0), Paint());
    canvas.drawImageNine(
        this.image,
        Rect.fromLTWH(0, 0, 120, 60),
        Rect.fromLTWH(
            60, 1020, image.width.toDouble() - 120, image.height.toDouble()),
        Paint());
    
    drawParagraph 绘制文字段落

          小菜以前认为 Canvas 不支持绘制文字,现在学习了 drawParagraph 完全可以绘制文字效果与 TextPainter 效果相同;

          文字段落 Paragraphdart.ui 中的类,用构造器方式进行内容绑定;ParagraphStyle 用来设置文字的样式属性,包括文字位置/方向/字体粗细/文字样式/行数等;其中 ellipsis 用来设置内容超出范围截取时最后展示内容,可随意编辑;

    ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle(
      textAlign: TextAlign.center,
      fontWeight: FontWeight.w600,
      fontStyle: FontStyle.normal,
      fontSize: 18,
    ))
      ..pushStyle(ui.TextStyle(color: Colors.blue))
      ..addText(
          'Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。');
    ParagraphConstraints pc = ParagraphConstraints(width: Screen.width - 60);
    Paragraph paragraph = pb.build()..layout(pc);
    canvas.drawParagraph(paragraph, Offset(30, 30));
    
    pb = ParagraphBuilder(ParagraphStyle(
      fontStyle: FontStyle.normal,
      fontWeight: FontWeight.w300,
      fontSize: 18,
    ))
      ..pushStyle(ui.TextStyle(color: Colors.red))
      ..addText(
          'Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。');
    pc = ParagraphConstraints(width: Screen.width - 60);
    paragraph = pb.build()..layout(pc);
    canvas.drawParagraph(paragraph, Offset(30, 180));
    
    pb = ParagraphBuilder(ParagraphStyle(
      fontStyle: FontStyle.normal,
      fontSize: 18,
      maxLines: 3,
      ellipsis: '...',
    ))
      ..pushStyle(ui.TextStyle(color: Colors.green))
      ..addText(
          'Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。');
    pc = ParagraphConstraints(width: Screen.width - 60);
    paragraph = pb.build()..layout(pc);
    canvas.drawParagraph(paragraph, Offset(30, 340));
    

    clipXXX

          以上介绍的都是绘制方法,接下来小菜简单介绍几种裁剪方法。

    clipRect 裁剪矩形

          clipRect 可以在规定的矩形内进行绘制,超出范围不绘制;

    canvas.clipRect(Rect.fromLTWH(30, 1200, Screen.width / 0.3 - 60, 300),
        doAntiAlias: false);
    canvas.drawImage(this.image, ui.Offset(260, 1200), Paint());
    
    clipRRect 裁剪圆角矩形

          clipRRect 可以在规定的圆角矩形内进行绘制,超出范围不绘制;

    canvas.clipRRect(
        RRect.fromRectXY(
            Rect.fromLTWH(300, 1600, image.width - 60.0, 300), 20, 20),
        doAntiAlias: false);
    canvas.drawImage(this.image, ui.Offset(260, 1200), Paint());
    
    clipPath 裁剪由线围成区域

          clipPath 可以在规定的点连线范围内进行绘制,默认终点与始点连接,当然可以绘制圆或贝塞尔曲线等,超出范围不绘制;

    canvas.clipPath(Path()
      ..moveTo(300, 100)..lineTo(900, 100)
      ..lineTo(800, 600)..lineTo(400, 600));
    canvas.drawImage(this.image, ui.Offset(260, 90), Paint());
    

          Canvas 真的非常强大,还有很多研究不透彻的地方,小菜还在不断学习,有错误的地方烦请多多指点;如下是小菜公众号,欢迎闲来吐槽~

    公众号.jpg

    相关文章

      网友评论

        本文标题:Flutter 35: 图解自定义 View 之 Canvas

        本文链接:https://www.haomeiwen.com/subject/wxeuuqtx.html