Flutter Canvas绘制文字和图片

一. Paragraph

void drawParagraph(Paragraph paragraph, Offset offset)

ParagraphFlutter中用于文字绘制的类,所有的文字最后都是通过它来绘制的。

Paragraph是一个没有构造函数的类,它只是提供一个宿主,用于最后的渲染。我们真正需要处理的是ParagraphBuilder这个类。

/// 1.生成 ParagraphStyle,可设置文本的基本信息
 final paragraphStyle = ui.ParagraphStyle(
  fontSize: 14,
  fontWeight: FontWeight.bold,
  textDirection: TextDirection.ltr,
);
/// 2.根据 ParagraphStyle 生成 ParagraphBuilder
final paragraphBuilder = ui.ParagraphBuilder(paragraphStyle);
/// 3.添加样式和文字
paragraphBuilder
  ..pushStyle(ui.TextStyle(color: Colors.black, fontSize: 16))
  ..addText('黑色文字')
  ..pushStyle(ui.TextStyle(color: Colours.red, fontSize: 14))
  ..addText('红色文字');
/// 4.通过 build 取到 Paragraph
final paragraph = paragraphBuilder.build();
/// 5.根据宽高进行布局layout
paragraph.layout(ui.ParagraphConstraints(width: 200));
/// 6.绘制
canvas.drawParagraph(paragraph, const Offset(50, 50));
drawParagraph.png

二. TextPainter

TextPainter是通过Paragraph封装而成,相比Paragraph它提供了更加强大的功能。

  • 通过传入TextSpan,实现多种不同效果的字体来支持富文本。
  • 不像Paragraph必须设置一个宽度,它可以不用初始化宽度,可以通过TextPainter().width来获取实际渲染宽度。
final textPainter = TextPainter()
  ..text = const TextSpan(text: '可多种不同效果的字体来支持富文本', style: TextStyle(color: Colors.white, fontSize: 20))
  ..textDirection = TextDirection.ltr
  /// 可以传入minWidth、maxWidth来限制宽度,若不传文字会绘制在一行
  ..layout(maxWidth: 100);

/// 绘制矩形框,在文字绘制前可通过textPainter.width和textPainter.height来获取文字绘制的尺寸
canvas.drawRect(
  Rect.fromLTWH(50, 50, textPainter.width, textPainter.height),
  Paint()..color = Colors.blue,
);
/// 绘制文字
textPainter.paint(canvas, const Offset(50, 50));
TextPainter.png

三.drawImage

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

这里的Imagedart:ui库中的Image,它保存了图片的一些基本信息并直接与引擎交互。

加载本地图片方法一:

Future<ui.Image> loadAssetImage(String asset) async {
  final imageData = await rootBundle.load(asset);
  final imageCodec = await ui.instantiateImageCodec(imageData.buffer.asUint8List());
  final imageInfo = await imageCodec.getNextFrame();
  return imageInfo.image;
}

加载本地图片方法二:

Future<ui.Image> _loadAssetImageByProvider(ImageProvider provider, {ImageConfiguration config = ImageConfiguration.empty}) async {
  final completer = Completer<ui.Image>();
  final stream = provider.resolve(config);
  late ImageStreamListener listener;
  listener = ImageStreamListener((frame, sync) {
    final image = frame.image;
    completer.complete(image);
    stream.removeListener(listener);
  });
  stream.addListener(listener);
  return completer.future;
}

/// 使用
_ loadAssetImageByProvider(AssetImage('assets/logo.png'))

/// 如果你的资源图片使用了分辨率加载(1.0x/2.0x/3.0x),不要使用rootBundle来加载图片,
/// rootBundle不会根据不同的上下文环境(比如不同的屏幕分辨率)加载不同版本的资源。
/// createLocalImageConfiguration中的DefaultAssetBundle类会结合当前的BuildContext来实例化与当前上下文环境相关联的AssetBundle对象。
_loadAssetImageByProvider(
  'assets/logo.png',
  config: createLocalImageConfiguration(context),
)

由于图片加载是异步过程,不能放在CustomPaintpaint方法中来加载,这里需要在外部使用一个StatefulWidget,加载完成后将获取到的Image传入CustomPaint中。此时再使用canvas.drawImage来绘制。

四. drawImageRect

void drawImageRect(Image image, Rect src, Rect dst, Paint paint)

  • src: 截取图片的一块区域,起始点相对于图片左上角。
  • dst:在canvas上的一块区域来绘制截取的图片,图片可能会被拉升。

可以用于实现图片缩放绘制:

if (logoImage != null) {
  /// 原图绘制
  canvas.drawImage(logoImage!, const Offset(50, 50), imagePaint);
  
  /// 缩小图片尺寸绘制
  canvas.drawImageRect(
    logoImage!,
    Rect.fromLTWH(0, 0, logoImage!.width.toDouble(), logoImage!.height.toDouble()),
    Rect.fromLTWH(50, 150, 74, 16),
    imagePaint,
  );
}
图片缩放绘制.png

五.drawImageNine

drawImageNine(Image image, Rect center, Rect dst, Paint paint)

  • center: 可拉伸或压缩的区域。
  • dst:在画布上绘制的目标矩形区域。

通过绘制两条水平线和两条垂直线将图像分割成9个部分,角落的4个区域会在不缩放的情况下被绘制在目标矩形,剩下的5个区域会通过拉伸或压缩来绘制,以便能够完全覆盖目标矩形。

六.drawPicture

通过传入一个Picture实例来进行绘制,而Picture需通过PictureRecorder来构造:

ui.Picture _getPictureFromCanvas() {
  // 图片记录仪,记录一系列图片操作
  final pictureRecorder = ui.PictureRecorder();
  /// 创建一个Canvas来记录
  final canvas = Canvas(pictureRecorder);
  /// 开始绘制
  canvas.drawCircle(
    const Offset(100, 100), 100,
    Paint()..color = Colors.red,
  );
  /// 结束记录,生成Picture
  final picture = pictureRecorder.endRecording();
  return picture;
}

drawPicture方法也是需要从外部传入一个Picture对象,不能在CustomPainerpaint方法中使用PictureRecorder来记录并绘制UI:

class MyCustomPainter extends CustomPainter {
  final ui.Picture? picture;
  
  MyCustomPainter(this.picture);

  @override
  void paint(Canvas canvas, Size size) {
    if (picture != null) {
      canvas.drawPicture(picture!);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true
}
drawPicture.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容