[Flutter][Canvas]Canvas画布绘制图片(一)

一、类型认识:

  1. assets资源(例如图片)转成ByteData ==》 例如:rootBundle.load(asset);可以由AssetBundle对象返回
  2. ByteData转成uint8List ==》 ByteDataObj.buffer.asUint8List()
  3. uint8list对象 转成 codec对象 ==》 instantiateimageCodec()
  4. codec对象 返回 FrameInfo ==》 Codec.getNextFrame()
  5. FrameInfo 对象 返回 ui.Image ==》 FrameInfo.image

二、AssetBundle 获取资源的类

继承AssetBundle 的类

- rootBundle.load("本地资源路径")
- NetworkAssetBundle(Uri.parse("网络资源路径")).load("网络资源路径");
- DefaultAssetBundle.of(context).load("本地资源路径");//只能在build中实用,因为需要传入context

返回的Future处理

 //返回Codec句柄
 ui.Codec codec = await ui.instantiateImageCodec(ByteData.buffer.asUint8List());
 //获取一帧图片FrameInfo
 ui.FrameInfo fi = await codec.getNextFrame();
 //获取ui.Image 
 ui.Image image = fi.image;

显示ui.Image对象

  1. 使用canvas类显示(不推荐)

    import 'dart:async';
    import 'dart:typed_data';
    import 'dart:ui' as ui;
    
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    class LALPageNews extends StatefulWidget {
      @override
      _LALPageNewsState createState() => _LALPageNewsState();
    }
    
    class _LALPageNewsState extends State<LALPageNews> {
      Uint8List imageMemory;//先定义一个uint8list对象
      @override
      Widget build(BuildContext context) {
        return SingleChildScrollView(
          child: Column(
            children: <Widget>[
              //点击按钮,点击后显示
              RaisedButton(
                  onPressed:() async{
                    ui.PictureRecorder pictureRecorder = new ui.PictureRecorder(); // 图片记录仪
                    Canvas canvas = new Canvas(pictureRecorder); //canvas接受一个图片记录仪
                    //第一个ui.image对象 给canvas.drawimage使用
                    ui.Image images = await getAssetImage('assets/images/time.jpg'); // 使用方法获取ui.Image格式的图片
                    Paint _linePaint = new Paint()
                      ..color = Colors.blue
                      ..style = PaintingStyle.fill
                      ..isAntiAlias = true//抗锯齿
                      ..strokeCap = StrokeCap.round//线条末端的处理方式
                      ..strokeWidth =20.0;
                    // 绘制图片
                    canvas.drawImage(images, Offset(0, 0), _linePaint); // 直接画图
    			  //第二个 ui.Image对象 由pictureRecorder结束记录后返回  toImage裁剪图片
                    ui.Image picture = await pictureRecorder.endRecording().toImage(MediaQuery.of(500, 1334);//设置生成图片的宽和高
                    //ByteData对象 转成 Uint8List对象 给 Image.memory() 使用来显示
                    ByteData pngImageBytes = await picture.toByteData(format: ui.ImageByteFormat.png);
                    //Uint8List imgBytes = Uint8List.view(pngImageBytes.buffer); //这一行和下面这一行都是生成Uint8List格式的图片
                    Uint8List pngBytes = pngImageBytes.buffer.asUint8List();
                    setState(() {
                      imageMemory = pngBytes;
                    });
                  },
                child: Text("生成图片"),
              ),
              imageMemory != null ? Image.memory(imageMemory) : Text('loading...'),
            ],
          ),
        );
      }
    }
    //返回ui.Image
    Future<ui.Image> getAssetImage(String asset,{width,height}) async {
      ByteData data = await rootBundle.load(asset);
      ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),targetWidth: 		width,targetHeight: height);
      ui.FrameInfo fi = await codec.getNextFrame();
     return fi.image;
    }
    

    备注:使用canvas对象,要传入一个pictureRecorder对象,pictureRecorder.endRecording()是绘画具体canvas图片的最后一步。

    1. 使用CustomPaint与CustomPainter widget显示(推荐)

      import 'dart:async';
      import 'dart:typed_data';
      import 'dart:ui' as ui;
      
      import 'package:flutter/material.dart';
      import 'package:flutter/services.dart';
      
      //方法1:获取网络图片 返回ui.Image
      Future<ui.Image> getNetImage(String url,{width,height}) async {
        ByteData data = await NetworkAssetBundle(Uri.parse(url)).load(url);
        ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),targetWidth: width,targetHeight: height);
        ui.FrameInfo fi = await codec.getNextFrame();
        return fi.image;
      }
      //方法2.1:获取本地图片  返回ui.Image 需要传入BuildContext context
      Future<ui.Image> getAssetImage2(String asset,BuildContext context,{width,height}) async {
        ByteData data = await DefaultAssetBundle.of(context).load(asset);
        ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),targetWidth: width,targetHeight: height);
        ui.FrameInfo fi = await codec.getNextFrame();
        return fi.image;
      }
      //方法2.2:获取本地图片 返回ui.Image 不需要传入BuildContext context
      Future<ui.Image> getAssetImage(String asset,{width,height}) async {
        ByteData data = await rootBundle.load(asset);
        ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),targetWidth: width,targetHeight: height);
        ui.FrameInfo fi = await codec.getNextFrame();
        return fi.image;
      }
      //方法3:通过stream类型来获取ui.Image 待更新
      
      //自定义CustomPainter(CustomPainter内不能使用异步,所有ui.Image通过实例化传入)
      //该widget做为CustomPaint的painter(在画布后面显示)或者foregroundPainter(在画布前面显示)参数。
      class SelfForePainter extends CustomPainter{
        ui.Image _imageFrame;
        SelfForePainter(this._imageFrame):super();
        @override
        void paint(Canvas canvas, Size size){
          Paint selfPaint = Paint()
            ..color = Colors.blue
            ..style = PaintingStyle.fill
            ..isAntiAlias = true
            ..strokeCap = StrokeCap.butt
            ..strokeWidth = 30.0;
          canvas.drawImage(_imageFrame, Offset(0,0), selfPaint);
        }
      
        @override
        bool shouldRepaint(CustomPainter oldDelegate) {
          return true;
        }
      }
      
      
      
      //在widget中显示
      class LALPageNews extends StatefulWidget {
        @override
        _LALPageNewsState createState() => _LALPageNewsState();
      }
      class _LALPageNewsState extends State<LALPageNews> {
        ui.Image _assetImageFrame;//本地图片
        ui.Image _netImageFrame;//网络图片
      
        @override
        void initState() {
          super.initState();
          _getAssetImage();
          _getNetImage();
        }
        //获取本地图片
        _getAssetImage() async{
          ui.Image imageFrame = await getAssetImage('assets/images/time.jpg',width: 200,height: 200);
          setState(() {
            _assetImageFrame = imageFrame;
          });
        }
        //获取网络图片
        _getNetImage() async{
          ui.Image imageFrame = await getNetImage('https://img.zcool.cn/community/0145f155452efa0000019ae95ef0e4.jpg@1280w_1l_2o_100sh.jpg',width: 200);
          setState(() {
            _netImageFrame = imageFrame;
          });
        }
        @override
        Widget build(BuildContext context) {
          return SingleChildScrollView(
            child: Column(
              children: <Widget>[
                CustomPaint(
                  foregroundPainter:SelfForePainter(_assetImageFrame),//画在child前面
                  child: Container(
                    width: double.infinity.toInt(),
                    height: 300,
                    color: Color.fromRGBO(155, 155, 155, 1),
                  ),
                ),
                CustomPaint(
               size: Size.fromWidth(double.infinity),
                  foregroundPainter:SelfForePainter(_netImageFrame),//画在child前面
                  child: Container(
                    width: double.infinity,
                    height: 300,
                    color: Color.fromRGBO(155, 155, 155, 0.1),
                  ),
             ),
              ],
            ),
          );
        }
      }
      

      备注:Custompaint作为widget来显示继承自CustomPainter的自定义类的Canvas,这种方法无需实例化Canvas和PictureRecorder,所以先对于上一种方案,更简单一些。

总结:静态资源路径或网络图片路径 可以转成ByteData类型;

  1. ByteDataObj.buffer.asUint8List() 转成 uint8List类型,这种类型 可以由 Image.memroy(uint8ListObj)直接转成图片显示(一般图片转成ByteData不会直接用Image.memroy显示,因为转成ByteData,就是为了更多的操作来修改图片,不然直接使用以下方法更直接。

    Image.asset(name)//显示本地图片
    Image.network(src)//显示网络图片
    
  2. ByteData ByteDataObj;
    //句柄(我的理解是,绘画一系列图片的对象,比如gif就是一系列图片的集合)
    ui.Codec codec = await ui.instantiateImageCodec(ByteDataObj.buffer.asUint8List(),targetWidth: width,targetHeight: height);
    //一帧数据
    ui.FrameInfo fi = await codec.getNextFrame();
    //帧数据变成ui.Image
    ui.Image image = fi.image;//ByteDataObj 对象可以转成 ui.image给CanvasObj.drawImage()作为参数
    //Canvas()需要PictureRecorder()来记录绘画动作,Canvas对象有可以绘画各种形状的方法;CanvasObj.drawImage()方法需要传入ui.Image【ui.Image与material中的Image不是一种widget】
    //CanvasObj其他draw方法就可以在图片上绘制其他形图案或者修改图片
    
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Flutter CanvasFlutter 框架提供的一个功能强大的绘图 API,它允许你直接在屏幕上绘制图形、文本和其他视觉元素。你可以使用 Flutter Canvas 创建自定义的 UI 控件、动画效果以及图表等。 通过 Flutter Canvas,你可以使用各种绘图方法来绘制形状、路径、渐变、图片和文本等。你可以使用 Canvas 类提供的方法来绘制直线、曲线、圆形、矩形等基本形状,还可以使用 Path 类来创建复杂的路径。同时,你也可以设置画笔的样式、颜色和透明度等属性,来实现各种视觉效果。 在 Flutter 中使用 Canvas,你需要在自定义的绘制方法中重写父类的 paint 方法,并将 Canvas 对象作为参数传入。然后,你可以在这个方法中使用 Canvas 对象提供的方法来绘制你想要的图形。 例如,下面是一个简单的 Flutter Canvas 绘制一个红色圆形的示例代码: ```dart import 'package:flutter/material.dart'; class MyCanvasWidget extends StatelessWidget { @override Widget build(BuildContext context) { return CustomPaint( painter: MyPainter(), ); } } class MyPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.red ..style = PaintingStyle.fill; final center = Offset(size.width / 2, size.height / 2); final radius = size.width / 4; canvas.drawCircle(center, radius, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } } ``` 这段代码定义了一个自定义的绘制 Widget `MyCanvasWidget`,并在其 `paint` 方法中使用 Canvas 绘制了一个红色的圆形。在 Flutter 的 UI 树中使用 `CustomPaint` 包裹这个自定义的绘制 Widget,即可将这个绘制效果显示在屏幕上。 希望这个简单的示例可以帮助你了解 Flutter Canvas 的基本使用方法。如果你有更多关于 Flutter Canvas 的问题,欢迎继续提问!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值