Flutter-drawAtlas圆形镂空

Zer*_*ive 6 dart flutter flutter-canvas flutter-custompainter

我正在尝试实现图像的圆形切口,并且我正在使用drawAtlas。这是到目前为止我的实现:

canvas.drawAtlas(
  image!,
  [
    /* Identity transform */
    RSTransform.fromComponents(
      rotation: 0.0,
      scale: 1,
      anchorX: 0,
      anchorY: 0,
      translateX: 0,
      translateY: 0,
    )
  ],
  [
    Rect.fromCircle(
      center: Offset(size.width / 2, size.height / 2),
      radius: 200,
    ),
  ],
  [],
  null,
  null,
  Paint(),
);
Run Code Online (Sandbox Code Playgroud)

虽然它确实有效,但它绘制了一个矩形图像。我想用一些画出同一图像的圆形切口strokeWidth。使用drawAtlas可以做到这一点吗?

use*_*613 3

很好的问题!尽管在您的代码中,您正在调用Rect.fromCircle指定剪切区域,但它仍然是一个矩形(您正在从圆形构造一个矩形)。正如您可能已经发现的那样,该drawAtlas方法仅支持从较大图像中剪切矩形区域。

这里的关键是使用canvas.clipRRect减少“允许绘画区域”。这类似于在 Photoshop 中添加选择框。执行此操作后,在选择框之外绘制的任何内容都将被忽略。换句话说,通过调用canvas.clipRRect,我们将无法再在剪切区域之外绘制任何内容。

据推测,绘制圆形图集后,您可能仍想在画布上绘制其他内容。对于其他事情,您可能不希望局限于剪裁的圆圈。为了解决这个问题,我们可以canvas.saveLayer在裁剪发生之前保存画布的状态,然后进行循环裁剪,然后绘制altas,最后调用canvas.restore恢复之前保存的状态。本质上,这将清除剪辑,使我们能够根据需要在画布上的任何位置绘制更多内容。

解决方案:

简而言之,paint 方法可能看起来像这样:

  void paint(Canvas canvas, Size size) {
    // Save the canvas state so we can clip it and then restore it later
    canvas.saveLayer(Rect.largest, Paint());
    // Clip the canvas, so we are only allowed to draw inside the circle
    canvas.clipRRect(
      RRect.fromRectAndRadius(
        Rect.fromLTWH(0, 0, 100, 100),
        Radius.circular(50),
      ),
    );
    // Draw the atlas (call your method)
    _drawAtlas(canvas, size);
    // Restore the canvas to its original state, so further drawings are not clipped
    canvas.restore();
  }
Run Code Online (Sandbox Code Playgroud)

演示:

演示图片

在演示中,当您按下按钮时,应用程序将截取灰色渐变容器内的 Flutter Logo 的屏幕截图,并将其用作数据ui.image。然后它将把drawAtlas图像的圆形部分绘制到画布上。

完整的演示代码(将所有内容粘贴到main.dart运行):

import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _globalKey = GlobalKey();

  ui.Image? _image;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: Column(
          children: [
            RepaintBoundary(
              key: _globalKey,
              child: Container(
                width: 300,
                height: 300,
                decoration: BoxDecoration(
                  gradient: RadialGradient(
                    colors: [Colors.white, Colors.grey],
                  ),
                ),
                child: FlutterLogo(),
              ),
            ),
            ElevatedButton(
              child: Text('Press Me'),
              onPressed: () async {
                final render = (_globalKey.currentContext!.findRenderObject()
                    as RenderRepaintBoundary);
                final image = await render.toImage();
                setState(() {
                  _image = image;
                });
              },
            ),
            if (_image != null)
              CustomPaint(
                size: Size(300, 300),
                painter: MyPainter(_image!),
              ),
          ],
        ),
      ),
    );
  }
}

class MyPainter extends CustomPainter {
  final ui.Image image;

  MyPainter(this.image);

  @override
  void paint(Canvas canvas, Size size) {
    // Save the canvas state so we can clip it and then restore it later
    canvas.saveLayer(Rect.largest, Paint());
    // Clip the canvas, so we are only allowed to draw inside the circle
    canvas.clipRRect(
      RRect.fromRectAndRadius(
        Rect.fromLTWH(0, 0, 100, 100),
        Radius.circular(50),
      ),
    );
    // Draw the atlas (call your method)
    _drawAtlas(canvas, size);
    // Restore the canvas to its original state, so further drawings are not clipped
    canvas.restore();
  }

  _drawAtlas(Canvas canvas, Size size) {
    canvas.drawAtlas(
      image,
      [
        /* Identity transform */
        RSTransform.fromComponents(
          rotation: 0.0,
          scale: 1,
          anchorX: 0,
          anchorY: 0,
          translateX: 0,
          translateY: 0,
        )
      ],
      [
        Rect.fromCircle(
          center: Offset(size.width / 2, size.height / 2),
          radius: 50,
        ),
      ],
      [],
      null,
      null,
      Paint(),
    );
  }

  @override
  bool shouldRepaint(oldDelegate) => true;
}
Run Code Online (Sandbox Code Playgroud)