【问题标题】:Flutter - How to draw an Image on Canvas using DrawImage methodFlutter - 如何使用 DrawImage 方法在 Canvas 上绘制图像
【发布时间】:2018-04-11 20:48:44
【问题描述】:

我正在尝试将图像文件绘制到画布中以在 Flutter 中组合我的小部件。

我确实关注了canvas documentation,但没有成功。哦Image docs,你说:

要获取 Image 对象,请使用 instantiateImageCodec。

我确实尝试过使用instantiateImageCodec 方法,但我只得到一个Codec 实例,而不是Image

如何使用canvas.drawImage在画布上绘制 ui.Image 实例的正确方法

这是我的代码片段:

Future<ui.Codec> _loadImage(AssetBundleImageKey key) async {
  final ByteData data = await key.bundle.load(key.name);
   if (data == null)
  throw 'Unable to read data';
   return await ui.instantiateImageCodec(data.buffer.asUint8List());
}

final Paint paint = new Paint()
  ..color = Colors.yellow
  ..strokeWidth = 2.0
  ..strokeCap = StrokeCap.butt
  ..style = PaintingStyle.stroke;

var sunImage = new ExactAssetImage("res/images/grid_icon.png");

sunImage.obtainKey(new ImageConfiguration()).then((AssetBundleImageKey key){
  _loadImage(key).then((ui.Codec codec){
    print("frameCount: ${codec.frameCount.toString()}");
    codec.getNextFrame().then((info){
      print("image: ${info.image.toString()}");
      print("duration: ${info.duration.toString()}");
      canvas.drawImage(info.image, size.center(Offset.zero), paint);
    });
  });
});

【问题讨论】:

    标签: android canvas dart flutter


    【解决方案1】:

    这个简单的实用方法返回一个Future&lt;UI.Image&gt;给定图像资产的路径:

    import 'dart:async';
    import 'dart:typed_data';
    import 'dart:ui' as UI;
    
    import 'package:flutter/services.dart';
    
    Future<UI.Image> loadUiImage(String imageAssetPath) async {
      final ByteData data = await rootBundle.load(imageAssetPath);
      final Completer<UI.Image> completer = Completer();
      UI.decodeImageFromList(Uint8List.view(data.buffer), (UI.Image img) {
        return completer.complete(img);
      });
      return completer.future;
    }
    

    【讨论】:

      【解决方案2】:

      ui.Codec 有一个方法getNextFrame(),它返回一个Future&lt;FrameInfo&gt;(你可能应该有关于多少帧的逻辑,但如果你知道它总是一张正常的图片,你可以跳过它。)FrameInfo 有一个@987654325 @ 属性,这是您需要的图像。

      编辑:查看您在帖子中的代码,不清楚您在哪里做什么。这都是在CustomPainter.paint 方法中定义的吗?如果是这样,您肯定会遇到问题,因为您只能在paint 通话期间使用canvas;您不应该在函数之外保留对它的任何引用(包括任何异步调用)。

      我建议使用 FutureBuilder,这样您只有在添加图像后才在画布上绘图。

      看起来像这样:

      Future<Image> _loadImage(AssetBundleImageKey key) async {
        final ByteData data = await key.bundle.load(key.name);
        if (data == null)
          throw 'Unable to read data';
        var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
        // add additional checking for number of frames etc here
        var frame = await codec.getNextFrame();
        return frame.image;
      }
      
      new FutureBuilder<Image>(
        future: loadImage(....), // a Future<String> or null
        builder: (BuildContext context, AsyncSnapshot<Image> snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.waiting: return new Text('Image loading...');
            default:
              if (snapshot.hasError)
                return new Text('Error: ${snapshot.error}');
              else
                // ImageCanvasDrawer would be a (most likely) statless widget
                // that actually makes the CustomPaint etc
                return new ImageCanvasDrawer(image: snapshot.data)
          }
        },
      )
      

      【讨论】:

      • 感谢回答我的问题!我确实根据您的建议更新了我的代码,现在我可以访问 Image 对象。但是当我尝试使用画布方法绘制时,颤动会用Object has been disposed. 抛出异常
      • 这是我的例外:
      • 查看我的编辑以获取使用 FutureBuilder 的示例。请注意,您可以使用 StatefulWidget 并简单地为图像传入 null 直到它被加载(并在画布中优雅地处理 null),然后将图像设置为 StatefulWidget,而不是使用 futurebuilder。
      • 我之前一直在问这样的问题,但你确定你真的想用画布手动绘制图像吗?如果这就是你想要做的所有事情,有很多更简单的方法可以在图像周围设置边框(或者你可能想要做的许多其他分类的事情,例如旋转、拉伸、定位等)。
      • 问题是 ui.image 导入。您应该提到从 ui.image 导入
      【解决方案3】:
      class ImagePainter extends CustomPainter {
        List<ui.Image> images = new List<ui.Image>();
        ImagePainter(
            {Key key,
            @required this.noOfSlice,
            @required this.images,
            @required this.rotation,
            this.boxfit = BoxFit.contain})
            :
              // : path = new Path()
              //     ..addOval(new Rect.fromCircle(
              //       center: new Offset(75.0, 75.0),
              //       radius: 40.0,
              //     )),
              tickPaint = new Paint() {
          tickPaint.strokeWidth = 2.5;
        }
        final int noOfSlice;
        final tickPaint;
        final BoxFit boxfit;
        ui.ImageByteFormat img;
        ui.Rect rect, inputSubrect, outputSubrect;
        Size imageSize;
        FittedSizes sizes;
        double radius,
            rotation = 0.0,
            _x,
            _y,
            _angle,
            _radiun,
            _baseLength,
            _imageCircleradius,
            _incircleRadius,
            _imageOffset = 0.0,
            _imageSizeConst = 0.0;
      
        @override
        void paint(ui.Canvas canvas, ui.Size size) {
          print("image data:: $images");
          radius = size.width / 2;
          _angle = 360 / (noOfSlice * 2.0);
          _radiun = (_angle * pi) / 180;
          _baseLength = 2 * radius * sin(_radiun);
          _incircleRadius = (_baseLength / 2) * tan(_radiun);
          if (noOfSlice == 4) {
            _imageOffset = 30.0;
            _imageSizeConst = 30.0;
            _x = 8.60;
            _y = 4.10;
          } else if (noOfSlice == 6) {
            _imageOffset = 20.0;
            _x = 10.60;
            _y = 5.60;
          } else if (noOfSlice == 8) {
            _imageOffset = 40.0;
            _imageSizeConst = 30.0;
            _x = 12.90;
            _y = 6.60;
          }
      
          //print("circle radisu:: $_incircleRadius");
      
          canvas.save();
          canvas.translate(size.width / 2, size.height / 2);
          canvas.rotate(-rotation);
          int incr = 0;
          rect = ui.Offset((size.width / _x), size.width / _y) & new Size(0.0, 0.0);
      
          imageSize = new Size(size.width * 1.5, size.width * 1.5);
          sizes = applyBoxFit(
              boxfit,
              imageSize,
              new Size(size.width / 2 * .50 + _incircleRadius * .8,
                  size.width / 2 * .50 + _incircleRadius * .8));
          inputSubrect =
              Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
          outputSubrect = Alignment.center.inscribe(sizes.destination, rect);
          if (images.length == noOfSlice && images.isNotEmpty)
            for (var i = 1; i <= noOfSlice * 2; ++i) {
              if (i % 2 != 0) {
                canvas.drawLine(
                  new Offset(0.0, 0.0),
                  new Offset(0.0, size.width / 2 - 4.2),
                  tickPaint,
                );
              } else {
                canvas.save();
                canvas.translate(-0.0, -((size.width) / 2.2));
                ui.Image image = images[incr];
                if (image != null) {
                  canvas.drawImageRect(
                      image, inputSubrect, outputSubrect, new Paint());
                }
      
                canvas.restore();
                incr++;
              }
              canvas.rotate(2 * pi / (noOfSlice * 2.0));
            }
          canvas.restore();
        }
      
        @override
        bool shouldRepaint(CustomPainter oldDelegate) {
          return false;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-14
        • 1970-01-01
        • 1970-01-01
        • 2011-04-20
        • 1970-01-01
        • 2019-09-20
        • 1970-01-01
        • 2020-09-03
        相关资源
        最近更新 更多