化学键绘BondDraw上架鸿蒙PC:不用 WebView 的纯 ArkTS 化学结构式编辑器
化学键绘BondDraw上架鸿蒙PC:不用 WebView 的纯 ArkTS 化学结构式编辑器
欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
化学结构式编辑器是最「值得重写」的一类移植对象:上游 JChemPaint 是 Java/Swing 生态,整套搬上鸿蒙既不可行也不划算;但它的领域模型——原子、化学键、环系、坐标清理——是纯粹的二维几何与图论,用 ArkTS 从模型到渲染完整重写,反而得到一个几百 KB、启动即画、零依赖的原生应用。本文按上架全流程复盘 BondDraw:领域模型怎么分层、Molfile 与 SMILES 双解析器怎么写、30° 角度吸附与双键平行线这类「专业感」怎么实现,以及 SDK 6.0.1 里
ImagePacker.pack被移除后的导出适配。
| 项目 | 上架信息 / 工程事实 |
|---|---|
| 应用名 | 化学键绘BondDraw |
| 包名 | org.jchempaint.jchempaint |
| 版本 | 1.1.0 (1000100) |
| 设备形态 | 2in1 / tablet(HarmonyOS PC) |
| 上游项目 | JChemPaint(开源化学结构绘制器,LGPL) |
| 技术路线 | 纯 ArkTS 全原生实现,无 WebView、无第三方引擎 |
| 源码规模 | 10 个 ArkTS 文件(模型 6 + 页面 3 + Ability 1) |
| 权限 | 零权限,文件与导出全部走系统选择器 |
| 上架状态 | 已在鸿蒙应用市场正式上架 |

一、先看效果图
图 1 · 模板库:经典分子一键加载

图 1:模板库面板。咖啡因、维生素 C、尼古丁等经典分子一键加载;左侧是键级工具(单键/双键/三键/未定义键/虚键)、原子与环系(三元环到八元环、苯环);底部元素栏 C/H/O/N/P/S/F/Cl/Br/I 与周期表入口。
图 2 · 网格辅助:每一根键都落在正确的角度上

图 2:30° 角度吸附、网格辅助、双键平行线与杂原子 CPK 配色。状态栏实时显示「103 原子 · 分子式 C8H10N4O2」——正在画的正是一分子咖啡因,画完即刻核对。
图 3 · 结构式绘制主界面

图 3:画布与工具栏。创建/打开/保存、撤销/重做、复制/粘贴、旋转/镜像/直角翻转、缩放一应俱全,完整键鼠交互。
二、需求设计:结构式绘制的「最小专业集」
化学课与实验报告需要随手画出规范的结构式。需求设计从「什么是规范」倒推出一张最小专业集清单——这也是纯 ArkTS 重写的功能边界:
| 必须有(专业底线) | 明确不做(首版边界) |
|---|---|
| 单/双/三键与未定义键、虚键 | 3D 构象与立体化学标注 |
| 原子标签、杂原子 CPK 配色 | 反应机理箭头推导 |
| 环系模板 + 经典分子模板 | 命名生成结构(IUPAC) |
| 30° 角度吸附 + 网格辅助 | 云端结构库检索 |
| Molfile / SMILES 双格式读写 | 多文档标签 |
| 一键结构清理(键角规整) | 原子属性编辑器面板 |
「不做清单」让首版聚焦在一条完整闭环上:画得出来 → 存得下去 → 导得出图。状态栏实时显示原子数、键数与 Hill 分子式,是这个闭环的「即时反馈器」——画完即核对,教学场景里它比任何功能按钮都常用。
三、架构:六个模型文件撑起的专业工具
纯 ArkTS 路线的代码组织刻意保持扁平——10 个文件,每层职责单一:
页面与交互层 Index.ets(画布、手势、菜单) / EditorController.ets(状态与命令)
领域模型层 ChemModel.ets 化学图模型(原子/键的邻接结构)
MdlIO.ets MDL Molfile 解析与写出
SmilesIO.ets SMILES 文本 → 化学图
Cleanup.ets 结构清理(键角规整)
Templates.ets 环系与经典分子模板库
渲染与输出层 Renderer.ets 自研二维绘制引擎
3.1 领域模型:化学图就是图论
ChemModel 把分子建模为标准图结构:原子是顶点(元素、坐标、电荷),键是带属性的边(键级、环标记)。这个决定让后续所有能力都「顺」了——
- Molfile 解析就是读一个「原子块 + 键块」的文本图描述;
- SMILES 解析是深度优先遍历 + 分支栈 + 环编号闭合;
- 结构清理是图上的几何优化;
- Hill 分子式是一次元素计数排序(C 在前、H 其次、其余按字母序)。
领域模型选对了,四个功能各自只剩几十行实现。
3.2 双解析器:专业格式的入场券
MdlIO 处理 MDL Molfile——化学软件界事实上的交换格式,counts line、原子块、键块逐行解析,坐标直接可用;写出时反向生成,保证画的东西能在其他化学软件里打开。
SmilesIO 处理 SMILES——CC(=O)O 这类一行文本描述整个分子。解析器是手写递归下降:原子符号、括号分支、环闭合编号三类语法,配合原子价态隐式氢补全。SMILES 的价值在输入效率:教学场景里老师给一串 SMILES 让学生「打开看看」,比点十次工具栏快得多。
3.3 Renderer:自研二维绘制的三个细节
化学结构式的「专业感」全在渲染细节里:
- 双键平行线:两条平行线段,偏移量随键长缩放,端点收缩让交叉处不穿帮;
- 杂原子标签避让:碳原子默认省略(骨架式),O/N/Cl 等杂原子显示标签;标签出现时键端要回缩一段文字避让区,否则线会穿过字母;
- CPK 配色:O 红、N 蓝、Cl 绿等约定色,键为黑灰、骨架线宽统一。
三个细节都是像素级的较真,但它们合起来决定了「这是化学软件」还是「这是画图板」。
四、交互实现:吸附、清理与模板
4.1 30° 角度吸附与网格辅助
结构式有强约定:链角 120°、环内几何对称。实现上把画布角度量化为 30° 步进,新键端点在放置时吸附到最近量化角;网格辅助负责平移对齐。这两个「小约束」让新手画出的图也接近专业软件的产出——吸附不是限制,是把领域规范内置成交互。
4.2 Cleanup:一键规整键角
手画的分子七扭八歪是常态。Cleanup 实现了几何规整:以键长为单位重建布局,链段按 120° 展开,环系按正多边形闭合。它是「结构清理」按钮背后的全部逻辑,也是模板功能的姊妹能力——模板给标准起点,Cleanup 给手画稿兜底。
4.3 Templates:从环系到咖啡因
模板库两层:环系模板(三元到八元环、苯环,含芳香环内圈)给局部构件;经典分子模板(咖啡因、维生素 C、尼古丁)给完整起点——每个都是一段内嵌 Molfile/SMILES 文本,走同一条解析链路加载。模板即数据,不特判:加载咖啡因和加载用户文件的代码路径完全一致,这保证解析器的正确性被模板持续回归。
4.4 缩放与画布
视图缩放以 ×1.25/×0.8 步进、钳位在 0.2–5.0 之间,复位一键回 1.0;画布平移与框选配合键鼠。首启刻意给空画布而非自动加载示例——工具类应用的首启要尊重「我有一个明确任务」的用户,示例入口放在模板面板里随取随用。
五、导出链路:SDK 6.0.1 的 API 变更适配
结构式画完通常要去贴进实验报告,导出 PNG 是闭环的最后一环,也是踩到 SDK 变更的地方:
// 1. 组件快照:把画布组件渲染成 PixelMap
const snapshot = await componentSnapshot.get('chemcanvas');
// 2. 图像编码:SDK 6.0.1(21) 中 ImagePacker.pack 已移除,改用 packToData
const packer = image.createImagePacker();
const packOpts: image.PackingOption = { format: 'image/png', quality: 100 };
const buf = await packer.packToData(snapshot, packOpts);
// 3. 系统保存对话框:用户指定落点,零存储权限
const opts = new picker.DocumentSaveOptions();
opts.newFileNames = ['structure.png'];
const uris = await (new picker.DocumentViewPicker(ctx)).save(opts);
fileIo.writeSync(fileIo.openSync(uris[0], READ_WRITE).fd, buf);
三个环节三个要点:组件快照用 componentSnapshot.get 按组件 id 截取(画布要设固定 id);编码 API 在 SDK 6.0.1 里从 pack 换成了 packToData——API 迁移期的旧写法在编译期就报错,这反而是好事,逼迫适配而不是静默降级;落盘走 DocumentViewPicker.save,导出即授权,权限表继续保持为空。
文件读写(打开/保存 .mol)同样走系统选择器,用户授权制,二次打开经由授权持久化免重复确认。
六、调试与真机实测
真机验收按「画一个完整分子」的剧本走查:
- 模板加载:一键载入咖啡因,状态栏原子/键数与 Hill 分子式逐项核对(103 原子、C8H10N4O2);
- 手绘 + 吸附:从苯环起手连支链,验证 30° 吸附与双键平行线的落点;
- 往返一致性:画 → 存 Molfile → 重新打开 → 与原图逐键比对;SMILES 输入
CC(=O)O生成乙酸后导出 Molfile,再用上游工具交叉验证; - 清理实测:故意画歪的链与环,Cleanup 后键角归位、无重叠;
- 导出实测:导出 PNG 真机落盘,图像清晰度满足报告插图要求;
- 异常路径:格式错误的 Molfile/SMILES 不崩溃,状态栏给出可读错误。
调试方法论上,渲染类问题的定位手段是把坐标数据可视化:Renderer 暴露调试开关,在画布上叠加绘制原子序号与键向量,吸附、避让、平行线偏移的每个几何量都看得见——几何 bug 的解药是让几何可见。
七、上架资质与审核沟通
| 材料 | 工程事实 |
|---|---|
| 身份与版本 | org.jchempaint.jchempaint,1.1.0 (1000100),签名 profile 绑定包名 |
| 权限表 | 零权限——绘制全本地,文件与导出走选择器授权 |
| 隐私面 | 无账号、无遥测、无网络;结构数据留在沙箱 |
| 预览图 | 模板库、网格辅助、主界面三张真机实拍 |
| 开源合规 | 上游 JChemPaint LGPL,应用内关于页声明一致 |
审核沟通的要点是把「纯 ArkTS 重写」讲成合规优势而非侵权风险:上游 LGPL 项目,重写实现的领域模型与格式规范不受版权保护,UI 与代码全部原创;关于页写明上游与许可,仓库 LICENSE 对齐。重写型移植的合规叙述要在提审前想清楚,避免审核阶段反复补充说明。
八、上架后迭代
1.1.0(versionCode 1000100)的迭代与后续路线围绕「教学场景加深」:
- 模板扩容:官能团与药物分子模板扩充,保持「模板即数据」架构不变;
- 更多格式:SMILES 写出(目前读入为主)与 InChI 读入,扩大互操作面;
- 原子属性编辑:电荷与同位素标注,为有机化学进阶场景铺路;
- 画布增强:多选批量操作与对齐分布。
每一条都建立在「领域模型是图论」的底座上——这就是当初把模型层独立出来的回报。
九、踩坑总结
- 领域模型选图论,解析/清理/统计全是顺产;模型层与渲染层严格分离,Renderer 只读模型。
- SMILES 手写递归下降可控:原子、括号分支、环闭合三类语法,几百行收口;隐式氢按价态补全别省。
- 杂原子标签必须回缩键端,否则线穿标签;骨架式省略碳标签是专业感的底线。
- 吸附要做成「放置时量化」而不是「事后校正」——交互语义完全不同,前者是帮助,后者是打扰。
- SDK 6.0.1 中
ImagePacker.pack已移除,导出用packToData;API 迁移让编译器替你把关。 - 首启给空画布,示例藏在模板面板;工具类应用尊重「带着任务来的用户」。
- 真机验收按完整剧本走(载入→手绘→清理→往返→导出→异常输入),逐项核对状态栏数据。
十、结语
BondDraw 证明了「重写」有时比「移植」更划算:当上游的 UI 框架(Java/Swing)注定带不上,而核心价值全在领域模型与格式规范里时,用目标平台的 first-class 语言重写,得到的是更小、更快、更贴合系统交互习惯的应用——几百 KB 的包体、零权限的模型、秒开的画布,全是重写的红利。
判断「该移植还是该重写」的两条标尺:一是上游价值在哪里(若在领域逻辑与文件格式,重写保值;若在交互资产与运行时,移植保值);二是目标平台的语言生态能否覆盖领域需求(ArkTS 的 Canvas、手势、组件快照恰好全覆盖)。两条都指向重写时,就不要犹豫。
源码与构建说明见开源仓库;本文代码摘录均来自工程实际提交。
更多推荐




所有评论(0)