前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter 像素编辑器#01 | 像素网格

Flutter 像素编辑器#01 | 像素网格

作者头像
张风捷特烈
发布2024-04-12 08:40:35
1560
发布2024-04-12 08:40:35
举报

本篇将完成如下功能:

  • [1]. 展示方形网格。
  • [2]. 通过网格的坐标信息,为像素单元格着色。
  • [3]. 通过手势交互,在网格中编辑像素点。
95.gif
95.gif

大家可以在 [码上掘金] 上体验,由 Flutter 构建的 web 版:

1. 绘制网格

首先,准备一下绘制面板的配置信息,通过 PixEditorConfig 类承载数据。目前可以配置行数、列数,绘制名称、颜色等。下面是 5*5 网格 和 8*8 网格的绘制效果:

5*5 网格

8*8网格

代码语言:javascript
复制
class PixEditorConfig {
  final int row; // 行
  final int column; // 列
  final String name; // 名称
  final Color backgroundColor; // 背景色
  final Color gridColor; // 网格颜色
  final bool showGrid; // 网格颜色

  PixEditorConfig( {
    required this.row,
    required this.showGrid,
    required this.column,
    required this.backgroundColor,
    required this.name,
    required this.gridColor,
  });
}

网格通过 CustomPainter 来自定义绘制,如下所示 PixEditPainter 中持有绘制的配置信息,在 paint 方法中根据配置信息通过 Canvas 进行绘制。其中网格的绘制逻辑封装为 drawGrid 方法,可以通过 config.showGrid 配置数据,决定是否绘制网格:

代码语言:javascript
复制
class PixEditPainter extends CustomPainter {
  final PixEditorConfig config;

  PixEditPainter({required this.config});

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect( Offset.zero & size, Paint()..color = config.backgroundColor);
    if(config.showGrid){
      drawGrid(canvas, size);
    }
  }
  
  @override
  bool shouldRepaint(covariant PixEditPainter oldDelegate) {
    return oldDelegate.config!=config;
  }
}

drawGrid 中根据行列数计算出每格的宽高,再通过移动和添加直线的方式操作路径。最后通过绘制 path 来展示网格。

代码语言:javascript
复制
  void drawGrid(Canvas canvas, Size size) {
    Paint girdPaint = Paint()..style = PaintingStyle.stroke..color = config.gridColor;
    Path path = Path();
    double stepH = size.height / config.row;
    for (int i = 0; i <= config.row; i++) {
      path.moveTo(0, stepH * i);
      path.relativeLineTo(size.width, 0);
    }
    double stepW = size.height / config.column;
    for (int i = 0; i <= config.column; i++) {
      path.moveTo(stepW * i, 0);
      path.relativeLineTo(0, size.height);
    }
    canvas.drawPath(path, girdPaint);
  }

二、根据坐标绘制像素

界面中网格的每格都有其对应的坐标,比如下面 5*5 网格 中坐标信息如下。我们希望做的就是通过坐标和颜色数据,为方格进行着色。下将对 (1,1) 坐标的网格着为蓝色:

image.png
image.png

这里将每个像素着色数据视为 PixCell,包含颜色和坐标两个数据:

代码语言:javascript
复制
class PixCell {
  final Color color;
  final (int x, int y) position;

  PixCell({
    required this.color,
    required this.position,
  });
}

在绘制时过程中,需要依赖 PixCell 列表数据。所以画板 PixEditPainter 中增加 List<PixCell> 列表成员:

代码语言:javascript
复制
class PixEditPainter extends CustomPainter {
  final PixEditorConfig config;
  final List<PixCell> pixCells;

  PixEditPainter({
    required this.config,
    required this.pixCells,
  });
  
  @override
  bool shouldRepaint(covariant PixEditPainter oldDelegate) {
    return oldDelegate.config != config || oldDelegate.pixCells != pixCells;
  }
}

然后封装一个 drawPixCells 方法绘制像素点。像素点是一个矩形,通过 PixCell 坐标可以确定矩形,然后使用 canvas.drawRect 绘制即可。

代码语言:javascript
复制
void drawPixCells(Canvas canvas, Size size){
  Paint cellPaint = Paint();
  double stepH = size.height / config.row;
  double stepW = size.height / config.row;
  for (int i = 0; i < pixCells.length; i++) {
    PixCell cell = pixCells[i];
    double top = cell.position.$1 * stepW;
    double left =  cell.position.$2 * stepH;
    Rect rect = Rect.fromLTWH(top , left, stepW, stepH);
    canvas.drawRect(rect.deflate(-0.2), cellPaint..color = cell.color);
  }
}

此时只要准备好 PixCell 数据列表,传递给画板,就可以为着色。比如下面准备了测试的列表数据:

5*5 网格

隐藏网格

代码语言:javascript
复制
[
  PixCell(color: Color(0xff5fc6f5), position: (0, 0)),
  PixCell(color: Color(0xff5fc6f5), position: (0 ,1)),
  PixCell(color: Color(0xff5fc6f5), position: (0, 2)),
  PixCell(color: Color(0xff5fc6f5), position: (0, 3)),
  PixCell(color: Color(0xff5fc6f5), position: (0, 4)),
  PixCell(color: Color(0xff5fc6f5), position: (1, 0)),
  PixCell(color: Color(0xff5fc6f5), position: (1, 2)),
  PixCell(color: Color(0xff5fc6f5), position: (3, 2)),
  PixCell(color: Color(0xff5fc6f5), position: (4, 3)),
  PixCell(color: Color(0xff5fc6f5), position: (2, 3)),
  PixCell(color: Color(0xff5fc6f5), position: (2, 1)),
  PixCell(color: Color(0xff5fc6f5), position: (4, 1)),
],

三、手势交互维护像素列表数据

最终,我们将通过手势交互来对网格像素进行着色或取消着色。当单元格有像颜色时,点击取消颜色,否则进行着色:

95.gif
95.gif

通过 GestureDetectoronTapDown 回调,可以监听到按下事件,其中可以得到点击时的触点坐标。我们需要将触点坐标转化为网格坐标,此时需要画板的尺寸,以及配置信息。点击事件由下面的 _handleTapDown 来处理:

  • 根据尺寸和和列数计算每格的宽高,然后通过触点计算落点在网格中的坐标。
  • 校验 pixCells 中是否存在当前网格坐标。如果由则移除该点,否则添加一个 PixCell。
  • 数据变化后,触发更新。
代码语言:javascript
复制
void _handleTapDown(TapDownDetails details, Size size, PixEditorConfig config) {
  double stepH = size.height / config.row;
  double stepW = size.height / config.row;
  int x = details.localPosition.dx ~/ stepW;
  int y = details.localPosition.dy ~/ stepH;
  bool hasPix = pixCells.where((e) => e.position == (x, y)).isNotEmpty;
  if (hasPix) {
    pixCells.removeWhere((e) => e.position == (x, y));
  } else {
    pixCells.add(PixCell(color: const Color(0xff5fc6f5), position: (x, y)));
  }
  pixCells = List.of(pixCells);
  setState(() {});
}

最后通过 LayoutBuilder 在构建过程中得到画板的区域,GestureDetector#onTapDown 回调触发 _handleTapDown 方法。CustomPaint 中使用 PixEditPainter 进行绘制:

image.png
image.png

到这里,第一版的 Flutter 像素编辑器就完成了,Flutter 的绘制能力可以应用于全平台。所以这个像素编辑器可以同时运行在 Android、iOS、Windows、MacOS、Linux、Web。目前只是一个非常简单的编辑像素功能,后续还会拓展更多的功能。敬请期待 ~

PS: 这不,github 的头像就有了

image.png
image.png
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 绘制网格
    • 二、根据坐标绘制像素
    • 三、手势交互维护像素列表数据
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档