Flutter - круглый вырез drawAtlas
Я пытаюсь реализовать круговой вырез изображения и использую для этого 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(),
);
Пока он работает, он рисует прямоугольное изображение. Я хочу нарисовать круглый вырез того же изображения с помощью некоторых
strokeWidth
. Можно ли это сделать с помощью DrawAtlas?
1 ответ
Отличный вопрос! Хотя в вашем коде вы звоните
Rect.fromCircle
чтобы указать область отсечения, но это все еще прямоугольник (вы строите прямоугольник из круга). Этот метод поддерживает только вырезание прямоугольных областей из большего изображения, как вы, вероятно, уже выяснили.
Ключевым моментом здесь является использование для уменьшения «разрешенной области рисования». Это похоже на добавление поля выбора в Photoshop. После этого все, что находится за пределами поля выбора, будет игнорироваться. Другими словами, позвонив
canvas.clipRRect
, мы больше не сможем рисовать что-либо за пределами обрезанной области.
Предположительно, после рисования круглого атласа вы все еще можете захотеть рисовать на холсте другие вещи. И для этих других вещей вы, вероятно, не захотите ограничиваться обрезанным кругом. Чтобы решить эту проблему, мы можем использовать
canvas.saveLayer
чтобы сохранить состояние холста до того, как произойдет отсечение, затем выполните круговое отсечение, затем нарисуйте альты, затем, наконец, вызовите
canvas.restore
для восстановления ранее сохраненного состояния. По сути, это очистит отсечение, что позволит нам рисовать больше вещей в любом месте на холсте, если это необходимо.
Решение:
Короче говоря, метод рисования может выглядеть примерно так:
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();
}
Демо:
В демоверсии, когда вы нажимаете кнопку, приложение делает снимок экрана с логотипом Flutter внутри серого градиентного контейнера и использует его в качестве
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;
}