像素画板应用


欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

一、项目概述

运行效果图
在这里插入图片描述
在这里插入图片描述

1.1 应用简介

像素画板应用是一款专注于像素艺术创作的绘图类应用,旨在帮助用户在手机上轻松绘制像素风格的艺术作品。应用以粉色为主色调,传递创意与活力的品牌形象。

应用涵盖了画笔、橡皮擦、填充、取色器、直线、矩形六大绘图工具,支持8x8到64x64多种画布尺寸,提供30种预设颜色选择。通过直观的触控操作和丰富的绘图功能,帮助用户释放创意,创作独特的像素艺术。

1.2 核心功能

功能模块 功能描述 实现方式
像素绘制 在网格上绘制像素 触控手势识别
工具切换 六种绘图工具切换 工具栏选择
颜色选择 选择绘画颜色 调色板+取色器
画布调整 调整画布大小 弹窗选择
撤销重做 撤销或重做操作 历史记录栈
导出图片 导出PNG图片 截图保存

1.3 绘图工具

序号 工具名称 功能描述 使用场景
1 画笔 绘制单个像素 自由绘制
2 橡皮擦 擦除像素 修改错误
3 填充 填充连续区域 快速上色
4 取色器 从画布取色 复用颜色
5 直线 绘制直线 几何图形
6 矩形 绘制矩形框 边框绘制

1.4 技术栈

技术领域 技术选型 版本要求
开发框架 Flutter >= 3.0.0
编程语言 Dart >= 2.17.0
设计规范 Material Design 3 -
绑定方式 CustomPainter -
目标平台 鸿蒙OS / Web API 21+

1.5 项目结构

lib/
└── main_pixel_art.dart
    ├── PixelArtApp           # 应用入口
    ├── DrawTool              # 绘图工具枚举
    ├── PixelArtHomePage      # 主页面
    ├── PixelPainter          # 像素绘制器
    └── _PixelArtHomePageState # 状态管理

二、系统架构

2.1 整体架构图

Data Layer

Business Logic Layer

Presentation Layer

主页面
PixelArtHomePage

工具栏

画布区域

调色板

工具选择

撤销重做

清空画布

像素网格

触控处理

颜色选择

自定义颜色

绘图引擎

历史管理

填充算法

像素矩阵
pixels

颜色调色板
palette

历史记录
history

2.2 类图设计

manages

uses

creates

renders

PixelArtApp

+Widget build()

«enumeration»

DrawTool

pencil

eraser

fill

picker

line

rect

+String label

+IconData icon

+String description

PixelArtHomePage

+Widget build()

_PixelArtHomePageState

-int _gridSize

-List<List<Color>> _pixels

-Color _currentColor

-DrawTool _currentTool

-List<List<List<Color>>> _history

-int _historyIndex

-bool _showGrid

+void _initPixels()

+void _saveToHistory()

+void _undo()

+void _redo()

+void _setPixel()

+void _fillArea()

+void _drawLine()

+void _drawRect()

PixelPainter

+List<List<Color>> pixels

+int gridSize

+bool showGrid

+void paint()

+bool shouldRepaint()

pixels

2.3 页面导航流程

画笔

橡皮擦

填充

取色器

直线

矩形

应用启动

主页面

工具栏

画布区域

调色板

选择工具

绘制模式

擦除模式

填充模式

取色模式

直线模式

矩形模式

触控绘制

当前工具

选择颜色

更新当前颜色

2.4 数据流向图

历史记录 像素矩阵 绘图引擎 画布 用户 历史记录 像素矩阵 绘图引擎 画布 用户 触控开始 传递坐标 判断当前工具 更新像素颜色 重绘界面 触控结束 保存当前状态 点击撤销 获取上一状态 恢复像素数据 重绘界面

三、核心模块设计

3.1 数据模型设计

3.1.1 像素矩阵
List<List<Color>> _pixels = [];

// 初始化像素矩阵
_pixels = List.generate(
  gridSize,
  (_) => List.generate(gridSize, (_) => Colors.transparent),
);
3.1.2 绘图工具枚举 (DrawTool)
enum DrawTool {
  pencil('画笔', Icons.edit, '绘制像素'),
  eraser('橡皮擦', Icons.cleaning_services, '擦除像素'),
  fill('填充', Icons.format_color_fill, '填充区域'),
  picker('取色器', Icons.colorize, '从画布取色'),
  line('直线', Icons.show_chart, '绘制直线'),
  rect('矩形', Icons.crop_square, '绘制矩形');
  
  final String label;
  final IconData icon;
  final String description;
}
3.1.3 历史记录结构
List<List<List<Color>>> _history = [];
int _historyIndex = -1;

// 保存到历史记录
void _saveToHistory() {
  _history = _history.sublist(0, _historyIndex + 1);
  _history.add(_pixels.map((row) => List<Color>.from(row)).toList());
  _historyIndex++;
  if (_history.length > 50) {
    _history.removeAt(0);
    _historyIndex--;
  }
}
3.1.4 画布尺寸分布
17% 17% 17% 17% 17% 17% 支持的画布尺寸 8x8 16x16 24x24 32x32 48x48 64x64

3.2 页面结构设计

3.2.1 主页面布局

PixelArtHomePage

AppBar

Body

标题

网格开关

画布大小

导出按钮

工具栏

画布区域

调色板

工具按钮组

撤销/重做

清空画布

3.2.2 画布区域结构

画布区域

GestureDetector

CustomPaint

PixelPainter

绘制像素

绘制网格

onPanStart

onPanUpdate

onPanEnd

onTapUp

3.2.3 调色板结构

调色板

当前颜色预览

颜色列表

点击打开选择器

水平滚动列表

30种预设颜色

选中状态高亮

3.3 绘图算法设计

3.3.1 填充算法流程

获取起始点

颜色相同?

结束

初始化队列

队列非空?

取出点

在边界内?

颜色匹配?

填充颜色

添加相邻点

3.3.2 直线绘制算法

获取起点终点

计算dx/dy

确定步进方向

初始化误差

到达终点?

结束

绘制当前点

更新误差

更新坐标

3.4 触控处理机制

onPanStart

画笔

橡皮擦

填充

取色器

直线/矩形

onPanUpdate

画笔

橡皮擦

onPanEnd

onTapUp

直线

矩形

触控事件

事件类型

获取起始坐标

当前工具

绘制像素

擦除像素

填充区域

获取颜色

记录起点

获取移动坐标

当前工具

保存历史

获取点击坐标

当前工具

绘制直线

绘制矩形


四、UI设计规范

4.1 配色方案

应用采用粉色为主色调,传递创意与活力的品牌形象:

颜色类型 色值 用途
主色 #E91E63 (Pink) 导航、强调元素
画布背景 #FFFFFF 绘图区域
网格线 #E0E0E0 像素分隔线
工具栏背景 #F5F5F5 工具栏底色
选中状态 PrimaryContainer 工具选中效果

4.2 字体规范

元素 字号 字重 颜色
页面标题 20px Medium #000000
工具提示 12px Regular #757575
弹窗标题 18px Medium #000000
按钮文字 14px Medium 主色

4.3 组件规范

4.3.1 工具栏
┌─────────────────────────────────────────────────────────────┐
│ [✏️] [🧹] [🪣] [💉] [📏] [▢]     ↩️ ↪️ 🗑️                    │
│  画笔 橡皮擦 填充 取色 直线 矩形   撤销 重做 清空              │
└─────────────────────────────────────────────────────────────┘
4.3.2 画布区域
┌─────────────────────────────────────┐
│ ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ │
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │
│ │        像素网格区域              │ │
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │
│ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ │
└─────────────────────────────────────┘
4.3.3 调色板
┌─────────────────────────────────────────────────────────────┐
│ ┌────┐  [■][■][■][■][■][■][■][■][■][■][■][■][■][■][■]...    │
│ │当前│   黑 白 红 粉 橙 黄 绿 青 蓝 靛 紫 棕 灰 ...          │
│ │颜色│                                                      │
│ └────┘                                                      │
└─────────────────────────────────────────────────────────────┘

五、核心功能实现

5.1 像素绘制实现

void _setPixel(int x, int y, Color color) {
  if (x >= 0 && x < _gridSize && y >= 0 && y < _gridSize) {
    setState(() {
      _pixels[y][x] = color;
    });
  }
}

5.2 填充算法实现

void _fillArea(int startX, int startY, Color newColor) {
  if (startX < 0 || startX >= _gridSize || startY < 0 || startY >= _gridSize) {
    return;
  }

  final targetColor = _pixels[startY][startX];
  if (targetColor == newColor) return;

  final queue = <Offset>[];
  final visited = <String>{};
  queue.add(Offset(startX.toDouble(), startY.toDouble()));

  while (queue.isNotEmpty) {
    final point = queue.removeAt(0);
    final x = point.dx.toInt();
    final y = point.dy.toInt();
    final key = '$x,$y';

    if (visited.contains(key)) continue;
    if (x < 0 || x >= _gridSize || y < 0 || y >= _gridSize) continue;
    if (_pixels[y][x] != targetColor) continue;

    visited.add(key);
    _pixels[y][x] = newColor;

    queue.add(Offset((x + 1).toDouble(), y.toDouble()));
    queue.add(Offset((x - 1).toDouble(), y.toDouble()));
    queue.add(Offset(x.toDouble(), (y + 1).toDouble()));
    queue.add(Offset(x.toDouble(), (y - 1).toDouble()));
  }

  setState(() {});
}

5.3 直线绘制实现(Bresenham算法)

void _drawLine(int x0, int y0, int x1, int y1, Color color) {
  final dx = (x1 - x0).abs();
  final dy = (y1 - y0).abs();
  final sx = x0 < x1 ? 1 : -1;
  final sy = y0 < y1 ? 1 : -1;
  var err = dx - dy;

  var x = x0;
  var y = y0;

  while (true) {
    if (x >= 0 && x < _gridSize && y >= 0 && y < _gridSize) {
      _pixels[y][x] = color;
    }

    if (x == x1 && y == y1) break;

    final e2 = 2 * err;
    if (e2 > -dy) {
      err -= dy;
      x += sx;
    }
    if (e2 < dx) {
      err += dx;
      y += sy;
    }
  }
  setState(() {});
}

5.4 矩形绘制实现

void _drawRect(int x0, int y0, int x1, int y1, Color color) {
  final minX = x0 < x1 ? x0 : x1;
  final maxX = x0 < x1 ? x1 : x0;
  final minY = y0 < y1 ? y0 : y1;
  final maxY = y0 < y1 ? y1 : y0;

  // 绘制上下边
  for (var x = minX; x <= maxX; x++) {
    if (x >= 0 && x < _gridSize) {
      if (minY >= 0 && minY < _gridSize) _pixels[minY][x] = color;
      if (maxY >= 0 && maxY < _gridSize) _pixels[maxY][x] = color;
    }
  }

  // 绘制左右边
  for (var y = minY; y <= maxY; y++) {
    if (y >= 0 && y < _gridSize) {
      if (minX >= 0 && minX < _gridSize) _pixels[y][minX] = color;
      if (maxX >= 0 && maxX < _gridSize) _pixels[y][maxX] = color;
    }
  }
  setState(() {});
}

5.5 触控处理实现

void _handlePanStart(DragStartDetails details, double cellSize) {
  final x = (details.localPosition.dx / cellSize).floor();
  final y = (details.localPosition.dy / cellSize).floor();

  if (x < 0 || x >= _gridSize || y < 0 || y >= _gridSize) return;

  switch (_currentTool) {
    case DrawTool.pencil:
      _setPixel(x, y, _currentColor);
      break;
    case DrawTool.eraser:
      _setPixel(x, y, Colors.transparent);
      break;
    case DrawTool.fill:
      _fillArea(x, y, _currentColor);
      _saveToHistory();
      break;
    case DrawTool.picker:
      setState(() {
        _currentColor = _pixels[y][x];
      });
      break;
    case DrawTool.line:
      _lineStart = Offset(x.toDouble(), y.toDouble());
      break;
    case DrawTool.rect:
      _rectStart = Offset(x.toDouble(), y.toDouble());
      break;
  }
}

5.6 历史记录实现

void _undo() {
  if (_historyIndex > 0) {
    setState(() {
      _historyIndex--;
      _pixels = _history[_historyIndex]
          .map((row) => List<Color>.from(row))
          .toList();
    });
  }
}

void _redo() {
  if (_historyIndex < _history.length - 1) {
    setState(() {
      _historyIndex++;
      _pixels = _history[_historyIndex]
          .map((row) => List<Color>.from(row))
          .toList();
    });
  }
}

5.7 自定义绘制器实现

class PixelPainter extends CustomPainter {
  final List<List<Color>> pixels;
  final int gridSize;
  final bool showGrid;

  PixelPainter({
    required this.pixels,
    required this.gridSize,
    required this.showGrid,
  });

  
  void paint(Canvas canvas, Size size) {
    final cellSize = size.width / gridSize;
    final paint = Paint();

    for (var y = 0; y < gridSize; y++) {
      for (var x = 0; x < gridSize; x++) {
        final color = pixels[y][x];
        paint.color = color;
        paint.style = PaintingStyle.fill;

        canvas.drawRect(
          Rect.fromLTWH(x * cellSize, y * cellSize, cellSize, cellSize),
          paint,
        );

        if (showGrid) {
          paint.color = Colors.grey.shade300;
          paint.style = PaintingStyle.stroke;
          paint.strokeWidth = 0.5;

          canvas.drawRect(
            Rect.fromLTWH(x * cellSize, y * cellSize, cellSize, cellSize),
            paint,
          );
        }
      }
    }
  }

  
  bool shouldRepaint(covariant PixelPainter oldDelegate) {
    return pixels != oldDelegate.pixels ||
        gridSize != oldDelegate.gridSize ||
        showGrid != oldDelegate.showGrid;
  }
}

六、交互设计

6.1 绘图交互流程

像素矩阵 绘图引擎 画布 用户 像素矩阵 绘图引擎 画布 用户 按下触控 onPanStart 计算像素坐标 设置像素颜色 移动触控 onPanUpdate 连续设置像素 抬起触控 onPanEnd 保存历史记录

6.2 工具切换流程

画笔/橡皮擦

填充

取色器

直线/矩形

点击工具按钮

更新_currentTool

重置临时状态

更新UI高亮

等待用户操作

工具类型

支持拖拽绘制

点击填充

点击取色

两次点击绘制

6.3 颜色选择流程

选择预设颜色

打开完整选择器

选择颜色

调色板显示

点击颜色

点击预览

更新当前颜色

颜色选择弹窗


七、扩展功能规划

7.1 后续版本规划

2024-01-07 2024-01-14 2024-01-21 2024-01-28 2024-02-04 2024-02-11 2024-02-18 2024-02-25 2024-03-03 2024-03-10 2024-03-17 2024-03-24 基础绘图 工具栏 颜色选择 图层功能 撤销重做优化 导出PNG 动画帧 作品分享 云端同步 V1.0 基础版本 V1.1 增强版本 V1.2 进阶版本 像素画板应用开发计划

7.2 功能扩展建议

7.2.1 图层功能

绘图能力增强:

  • 多图层支持
  • 图层可见性控制
  • 图层顺序调整
  • 图层合并
7.2.2 动画功能

动画创作支持:

  • 帧动画制作
  • 洋葱皮预览
  • GIF导出
  • 帧率调整
7.2.3 社交功能

分享与互动:

  • 作品分享
  • 评论点赞
  • 创作者关注
  • 热门推荐

八、注意事项

8.1 开发注意事项

  1. 坐标计算:触控坐标需要转换为像素网格坐标

  2. 边界检查:所有像素操作前需检查边界

  3. 历史记录:每次操作后保存状态,限制最大历史数量

  4. 性能优化:大面积填充时使用队列而非递归

8.2 常见问题

问题 原因 解决方案
绘制位置偏移 坐标计算错误 检查cellSize计算
填充不完整 边界判断错误 检查边界条件
撤销无效 历史未保存 确保调用_saveToHistory
网格显示异常 shouldRepaint错误 检查重绘条件

8.3 使用提示

🎨 像素画板使用指南 🎨

画笔工具:按住并拖动绘制连续像素。
橡皮擦:按住并拖动擦除像素。
填充工具:点击区域填充连续相同颜色。
取色器:点击画布上的像素获取颜色。
直线工具:点击起点,再点击终点绘制直线。
矩形工具:点击起点,再点击终点绘制矩形框。


九、运行说明

9.1 环境要求

环境 版本要求
Flutter SDK >= 3.0.0
Dart SDK >= 2.17.0
鸿蒙OS API 21+

9.2 运行命令

# 查看可用设备
flutter devices

# 运行到Web服务器
flutter run -d web-server -t lib/main_pixel_art.dart --web-port 8114

# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_pixel_art.dart

# 运行到Windows
flutter run -d windows -t lib/main_pixel_art.dart

# 代码分析
flutter analyze lib/main_pixel_art.dart

十、总结

像素画板应用通过简洁直观的界面设计,为用户提供了一个便捷的像素艺术创作平台。应用涵盖了画笔、橡皮擦、填充、取色器、直线、矩形六大绘图工具,支持8x8到64x64多种画布尺寸,提供30种预设颜色选择。

核心功能涵盖像素绘制、工具切换、颜色选择、画布调整、撤销重做、导出图片六大模块。像素绘制通过触控手势识别实现;工具切换提供直观的工具栏选择;颜色选择支持调色板快速选择和取色器从画布取色;画布调整支持多种预设尺寸;撤销重做通过历史记录栈实现;导出图片支持PNG格式输出。

应用采用Material Design 3设计规范,以粉色为主色调,界面简洁活力。通过本应用,希望能够帮助用户释放创意,轻松创作独特的像素艺术作品。

用像素描绘创意,让想象变成艺术

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐