持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情
- 「手绘板的制作——手绘(1)」
- 「手绘板的制作——重置与橡皮擦(2)」
- 「手绘板的制作——命令模式与撤销、重制(3)」
- 「手绘板的制作——画布缩放(4)」
- 「手绘板的制作——画布移动(5)」
- 「手绘板的制作——画布保存(6)」
前言
在上一篇文章「手绘板的制作——手绘(1) 」中,我们完成了手绘的功能,这一篇我们在其基础上来讲讲重置与橡皮擦的功能实现。
在讲具体的功能实现前,我们需要先弄几个文本,用于笔刷、重置、橡皮擦的功能切换,同时把手绘板的功能抽取出来,放到 HandPaintedBoard
类中,大致代码如下,由于大多都是 UI 代码,可以直接初略看看即可,若需要自己实践再进行 copy。
void main() {
runApp(const MyHomePage());
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final PaintedBoardProvider _paintedBoardProvider = PaintedBoardProvider(); // <- 重点在这里
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
color: Colors.white,
child: SafeArea(
child: Flex(
direction: Axis.vertical,
children: [
SizedBox(
height: 100,
child: Flex(
direction: Axis.horizontal,
children: [
Expanded(
child: GestureDetector(
onTap: () {
print("点击了笔刷");
},
child: const Center(
child: Text("笔刷"),
),
),
),
Expanded(
child: GestureDetector(
onTap: () {
print("点击了重置");
},
child: const Center(
child: Text("重置"),
),
),
),
Expanded(
child: GestureDetector(
onTap: () {
print("点击了橡皮擦");
},
child: const Center(
child: Text("橡皮擦"),
),
),
),
],
),
),
Expanded(child: HandPaintedBoard(_paintedBoardProvider)), // <- 重点在这里
],
),
),
),
),
);
}
}
复制代码
class HandPaintedBoard extends StatefulWidget {
const HandPaintedBoard(
this._paintedBoardProvider, {
Key? key,
}) : super(key: key);
final PaintedBoardProvider _paintedBoardProvider;
@override
_HandPaintedBoardState createState() => _HandPaintedBoardState();
}
class _HandPaintedBoardState extends State<HandPaintedBoard> {
PaintedBoardProvider get _paintedBoardProvider =>
widget._paintedBoardProvider;
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanStart: (details) {
_paintedBoardProvider.onStart(details);
},
onPanUpdate: (details) {
_paintedBoardProvider.onUpdate(details);
},
onPanEnd: (details) {
print("onPanDown:移动结束");
},
child: CustomPaint(
painter: MyPainter(_paintedBoardProvider),
size: Size.infinite,
),
);
}
}
复制代码
至于 stroke.dart
、painted_board_provider.dart
、my_painter.dart
在 「手绘板的制作——手绘(1) 」中都有,这里就不贴出了。
UI 效果:
然后我们点击三个按钮试试:
I/flutter: 点击了笔刷
I/flutter: 点击了重置
I/flutter: 点击了橡皮擦
复制代码
准备工作完成,正文开始!
重置
重置功能其实就是将当前绘画的内容全部清空,而我们用于存储绘画数据的为 PaintedBoardProvider
中的 List<Stroke> _strokes
,所以,我们只要将其清空,然后刷下页面即可。
在 PaintedBoardProvider
中添加以下方法:
void clearBoard() {
_strokes.clear();
notifyListeners();
}
复制代码
然后在点击的地方调用:
Expanded(
child: GestureDetector(
onTap: () {
print("点击了重置");
_paintedBoardProvider.clearBoard();
},
child: const Center(
child: Text("重置"),
),
),
复制代码
ok,功能完成,就是这么简单,下一个。
橡皮擦
关于橡皮擦功能的实现,目前有两种方式:
- 假清除效果,也就是把画笔颜色改为白色,或者说改为跟画布同一个颜色,然后这样绘画的时候,就会将原有的画笔绘画效果覆盖住,造成一种清除效果的假象。所以这里不建议这样使用,有兴趣的可以自己实现下。
- 真清除效果,也就是把画笔颜色改为透明,然后用画笔进行绘画的时候,当画笔绘画的内容产生交接时,将其
blendMode
改为clear
,就可以把交接处直接清除。
下面我们来讲讲真清除效果。
// 获取绘画数据进行绘画
for (final stroke in paintedBoardProvider.strokes) {
final paint = Paint()
..strokeWidth = stroke.width
..color = stroke.color
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..blendMode = stroke.isClear ? BlendMode.clear : BlendMode.src; // <- 新增
canvas.drawPath(stroke.path, paint);
}
复制代码
看看效果:
em...怎么变成黑色了?虽然说我们是直接在画布上绘画,即使是把背景清除了,也不应该是黑色,因为我也尝试过使用橙色背景的 Container
包裹住 CustomPaint
,但是画出的颜色还是黑色。对此了解的大佬麻烦解答下。
至于解决办法我倒是了解,也就是使用 saveLayer
。相当于我们在使用 Photoshop 的时候,新建一个透明蒙层,在新的蒙层上进行绘画,等绘画完成后,我们可以调用 restore
将该蒙层贴回画布中,重新在画布上进行绘制。大致的代码如下:
canvas.saveLayer(Rect.fromLTWH(0, 0, size.width, size.height), Paint());
// 获取绘画数据进行绘画
for (final stroke in paintedBoardProvider.strokes) {
final paint = Paint()
..strokeWidth = stroke.width
..color = stroke.color
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..blendMode = stroke.isClear ? BlendMode.clear : BlendMode.src; // <- 新增
canvas.drawPath(stroke.path, paint);
}
canvas.restore();
复制代码
效果: