发布时间:2026/7/13 6:40:47
bitArt 系列 01:架构 最近整理一个 HarmonyOS 像素画编辑器项目 bitArt发现它很适合作为复杂 ArkTS 应用的拆解案例。它不是一个只有按钮和列表的 Demo而是一个完整的移动端创作工具中间是像素画布周围有工具栏、图层面板、调色板、动画帧时间轴、项目管理、导入导出和设置。这篇先不急着讲某一个算法而是从整体架构看它如何把复杂编辑器拆开。源码路径是D:\HM\bitArt核心代码集中在entry/src/main/ets。1. 为什么编辑器应用不能只写在页面里像素画编辑器看起来是 UI 密集型应用但真正麻烦的地方不是按钮多而是状态多。一个用户在画布上移动手指时应用至少要处理这些事判断当前工具是铅笔、橡皮、填充、取色、形状还是移动。把触控坐标转换成像素坐标。判断当前图层是否锁定。修改当前图层的像素矩阵。重新合成可见图层。更新画布渲染。记录撤销快照。标记项目已修改等待自动保存。如果时间轴打开还要同步当前帧。如果这些逻辑全部塞进一个页面的 UI 构建代码里页面会很快变成一个几千行的状态泥潭。bitArt 当前把入口、工作区组件和领域服务分开Index.ets只处理页面可见性并挂载工作区BitArtEditorWorkspace.ets负责状态编排具体行为继续下沉到services/。项目核心目录大致是entry/src/main/ets/ pages/ Index.ets components/editor/ BitArtEditorWorkspace.ets PixelCanvasView.ets models/ EditorModels.ets services/ PixelCanvasService.ets PixelCanvasRendererService.ets LayerCanvasService.ets LayerStackService.ets EditorCanvasDrawingWorkflowService.ets EditorHistoryWorkflowService.ets EditorFrameService.ets EditorPersistenceService.ets EditorExportService.ets ImageImportService.ets这个拆分的好处是非常直接的画布算法、图层算法、帧算法、保存算法都可以独立阅读和测试。页面不需要知道“一个 GIF 怎么编码”也不需要知道“source-over 混合公式怎么写”它只需要调用服务层拿到新的状态。2. 工作区组件BitArtEditorWorkspace 是状态编排器Index.ets当前是一个很薄的页面壳页面出现或隐藏时更新editorPageVisiblebuild()中只挂载BitArtEditorWorkspace()。真正持有当前工具、颜色、图层、帧、画布尺寸、面板开合、撤销栈和项目列表的是BitArtEditorWorkspace.ets。例如工作区组件初始化图层和帧时并不手工拼像素数组const INITIAL_LAYERS_PIXELS: string[][] LayerCanvasService.createInitialLayers(LAYERS, DEFAULT_CANVAS_SIZE, DEFAULT_CANVAS_SIZE); const INITIAL_FRAMES: EditorFrame[] EditorFrameService.createInitialFrames(LAYERS, INITIAL_LAYERS_PIXELS, layer-1);这段代码背后的设计点是页面不直接制造像素矩阵而是把“初始图层应该是什么样”交给LayerCanvasService把“第一帧应该如何复制图层数据”交给EditorFrameService。换句话说BitArtEditorWorkspace更像一个工作台调度器它决定当前展示哪些面板。它把用户操作转换成服务层调用。它接收服务返回的新状态。它触发 UI 刷新、保存和导出。这种拆分对编辑器应用很重要。因为编辑器的功能会持续增长如果页面从一开始就承担所有业务细节后面新增图层合并、动画帧、GIF 导出时代码会很难维护。3. 组件层PixelCanvasView 专心处理画布交互bitArt 把真正的 Canvas 组件放到了components/editor/PixelCanvasView.ets。这个组件关注的是“画布如何显示”和“触摸如何转成像素坐标”。它接收像素数据、画布宽高、缩放比例、脏区域边界等属性Prop Watch(handlePixelsChange) pixels: string[] []; Prop Watch(redraw) gridVisible: boolean true; Prop Watch(handleCanvasSizeChange) canvasWidth: number 24; Prop Watch(handleCanvasSizeChange) canvasHeight: number 24; Prop Watch(handleDirtyVersionChange) dirtyVersion: number 0; Prop dirtyMinRow: number 0; Prop dirtyMinCol: number 0; Prop dirtyMaxRow: number 0; Prop dirtyMaxCol: number 0;这里有一个值得借鉴的点组件没有自己去管理项目、图层、帧它只认最终要画出来的pixels。这样 Canvas 组件就可以保持纯粹复用成本也更低。用户触摸画布时组件把屏幕坐标转换成像素行列再通过回调通知页面private getTouchCell(event: TouchEvent): CanvasCell { const x event.touches[0].x - this.getDrawLeft(); const y event.touches[0].y - this.getDrawTop(); return { row: EditorViewportService.positionToCell(y, drawHeight, this.canvasHeight), col: EditorViewportService.positionToCell(x, drawWidth, this.canvasWidth), valid: true }; }组件层只负责把“用户点了哪里”说清楚至于点下去以后是画笔、橡皮、填充还是取色交给上层状态和服务层决定。4. 服务层复杂逻辑按领域拆开bitArt 的services/是项目最值得看的一层。几个核心服务的职责如下服务职责PixelCanvasService单层像素矩阵操作负责画笔、橡皮、填充、形状、取色PixelCanvasRendererServiceCanvas 渲染负责网格、脏区域、像素柔化和同色连续段批绘LayerCanvasService多图层像素合成负责透明度混合和局部合成LayerStackService图层栈操作负责新增、删除、复制、锁定、合并EditorFrameService动画帧操作负责帧复制、删除、时长归一化EditorPersistenceService项目持久化负责 manifest 和项目 JSON自动保存时机由会话工作流控制EditorExportService图片导出负责 PNG/JPG/GIF 和相册写入ImageImportService图片导入负责相册选择、解码、缩放和转像素矩阵这个服务划分的关键是“按领域而不是按按钮拆”。比如图层面板上可能有删除、复制、锁定、透明度调整等多个按钮但它们都属于图层栈行为所以统一放在LayerStackService。5. 模型层让状态可以保存、恢复和撤销bitArt 的模型集中在EditorModels.ets。核心模型包括export interface EditorFrame { id: string; name: string; durationMs: number; layers: LayerItem[]; layersPixels: string[][]; activeLayer: string; } export interface EditorPersistentProject { schemaVersion: number; projectId: string; projectName: string; canvasWidth?: number; canvasHeight?: number; activeTool: string; activeLayer: string; activeFrame: string; frames: EditorFrame[]; layers: LayerItem[]; layersPixels: string[][]; undoStack: EditorSnapshot[]; redoStack: EditorSnapshot[]; }这里可以看到一个重要选择帧不是只保存一张预览图而是保存完整的layers和layersPixels。这意味着每一帧都有独立图层结构动画编辑不会和当前画布状态混在一起。对于编辑器来说这种数据完整性比省一点内存更重要。否则切换帧、复制帧、导出 GIF 时很容易出现“当前画面和帧数据不同步”的问题。6. 系统能力层HarmonyOS API 的落点bitArt 用到的 HarmonyOS 能力主要集中在几个地方kit.AbilityKit拿到UIAbilityContext用于文件路径和应用上下文。kit.CoreFileKit读写项目 JSON、缓存导出图片。ohos.multimedia.image创建 PixelMap、编码 PNG/JPG。ohos.file.photoAccessHelper选择图片、写入系统相册。AppStorage同步安全区、持久化触发信号等跨层状态。这种系统 API 不直接散落在页面里的做法也很稳。比如保存项目时只需要看EditorPersistenceService导出图片时只需要看EditorExportService。后续如果 HarmonyOS API 有变化修改范围也更明确。7. 怎么判断分层没有停留在目录表面文件分得多不等于架构清晰。验证 bitArt 的分层可以沿一条真实操作向下追踪用户在画布落笔后PixelCanvasView只上报触控位置绘制工作流决定当前工具和脏区域像素服务修改活动层图层服务更新合成结果工作区负责替换状态、记录历史并标记项目已修改。每一层都有输入和输出没有让 Canvas 组件直接写项目文件也没有让持久化服务反过来操作 UI。再检查几个跨模块动作切换动画帧前是否同步工作区图层导出前是否结束笔触并强制保存恢复项目后是否重新建立活动帧和活动图层锁定图层后绘制工作流是否在修改像素前拦截。只要这些路径能顺着服务边界走完分层才真正参与了行为控制而不只是把长文件拆成多个短文件。8. 这个架构最值得借鉴的点我认为 bitArt 的整体设计有三个经验可以复用到其他 HarmonyOS 应用。第一页面层不要吞掉业务层。复杂应用早期看起来可以靠一个页面撑住但状态一多维护成本会急剧上升。第二编辑器应用要先定义数据所有权。bitArt 让项目、帧、图层、像素矩阵都有明确归属之后撤销、保存、导出才能顺畅接上。第三高频交互要单独设计渲染路径。像素画布不是普通列表它有连续触摸、缩放、平移、局部刷新和网格渲染。如果直接依赖全量刷新体验很快会掉下来。这一篇主要把项目全貌铺开。下一篇会继续拆EditorModels.ets和EditorCanvasService.ets看一个像素画编辑器的数据模型如何设计尤其是为什么 bitArt 选择LayerItem[] string[][]这种结构来承载图层像素。

相关新闻

2026/7/13 6:40:47

专业铸就口碑!细数黑龙江移远科技的核心服务优势

在黑龙江无线通信行业,供应商数不胜数,但能长期稳定服务政企、景区、林场、油田、市政、工程企业的本土品牌,黑龙江移远科技始终凭借专业、靠谱、本土化的服务优势,赢得市场认可与客户信赖。第一大优势:深耕本土&#…

2026/7/13 6:40:46

AI视频生成技术实战:从扩散模型到工程化应用全解析

最近在技术圈里,一个名为"2026红尘CK冲榜宣传片"的项目引起了广泛讨论。很多开发者第一眼看到这个标题时都会感到困惑——这到底是游戏宣传片、影视项目,还是某种新型的技术应用?实际上,这个项目背后涉及的是当前最热门…

2026/7/13 6:35:46

蓝牙5.4音频模块开发实战:低功耗LC3编码与射频设计

1. 项目背景与核心组件选型在无线音频传输领域,蓝牙5.4标准的推出标志着LE Audio技术正式进入实用阶段。我们选择的IDC777-1模块作为射频前端,搭配STM32L152RE主控的方案,能够实现专业级无线音频传输质量。这套组合的核心优势在于&#xff1a…

2026/7/13 8:10:53

Qt 6.5.3 项目打包实战:windeployqt + Enigma Virtual Box 制作 1 个独立 EXE

Qt 6.5.3 跨平台打包全流程:从依赖管理到单文件封装实战在桌面应用开发领域,Qt框架因其卓越的跨平台能力和丰富的功能模块而广受欢迎。但当开发完成后,如何将Qt应用程序打包成可独立分发的形式,特别是制作成单一可执行文件&#x…

2026/7/13 8:10:53

pandas多维聚合实战:滚动计算与业务可解释性设计

1. 项目概述:为什么多维聚合不是“加个groupby”就能搞定的事我在银行数据平台组干了八年,从最早用SQL写几十行嵌套子查询做客户分层,到现在每天在Jupyter里调试pandas的agg链式调用,踩过的坑比写的代码还多。今天这篇讲的“多维聚…

2026/7/13 8:05:53

Windows 10系统安装全攻略:从镜像下载到优化配置

你的电脑运行越来越慢,频繁蓝屏,或者被各种预装软件拖累?去电脑城重装系统动辄上百元,还可能遇到捆绑软件和隐私风险。其实,Windows 10系统安装远没有想象中复杂,掌握正确方法后,任何人都能轻松…

2026/7/13 6:38:38

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾在网易云音乐下载了心爱的歌曲&#…

2026/7/12 0:01:29

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境随着新能源汽车行业的快速发展,充电通信协议的标准化和测试验证变得尤为重要。GB/T 27930-2023作为中国智能充电协议的最新版本,对充电机与电动汽车之间的通信提出了更严格…

2026/7/12 0:01:29

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡 【免费下载链接】rtl8852be Realtek Linux WLAN Driver for RTL8852BE 项目地址: https://gitcode.com/gh_mirrors/rt/rtl8852be 还在为Linux系统无法识别RTL8852BE Wi-Fi 6网卡而烦恼吗?&#x1f…

2026/7/13 0:00:24

广氟 PTFE 高速线缆膜 —— 高端线缆绝缘材料新选择

PTFE高速线缆膜的基本概念与特点 PTFE 高速线缆膜是以聚四氟乙烯树脂为原料,经膨化双向拉伸制成的多孔绝缘薄膜,作为高速高频通信线缆的核心介质材料,内部形成均匀连通的微孔结构,兼具极低介电常数与介电损耗,能有效降…

2026/7/12 11:21:32

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…