鸿蒙PC平台宅匠 3D 室内设计适配实战:基于 Electron 壳方案的 Three.js 双渲染引擎与参数化户型编辑器开发

宅匠 3D 室内设计是一款纯 JavaScript 实现的户型编辑与三维可视化工具,提供 24 种家具预设(含 3D 造型件描述)、参数化墙体系统(门窗沿墙定位)、8 种绘图模式(墙体/门窗/房间/家具/标注/文本/选择)、Three.js WebGL 3D 渲染 + Canvas 2D 等轴测软件渲染双引擎、鸟瞰与漫游双视角、撤销重做、文件存取与 PNG 导出等完整室内设计能力。本文记录在鸿蒙 PC 平台上,基于 Electron 壳方案从零构建一个具备 2D 户型编辑、3D 装配可视化、家具拖拽放置、双渲染引擎自动切换的完整过程------涵盖参数化墙体系统、画家算法软件渲染、层级命中检测、JSON 序列化校验、grid 同格叠加弹层、以及鸿蒙安全适配等核心功能的实现细节与踩坑经验。

欢迎加入开源鸿蒙 PC 社区:https://harmonypc.csdn.net/

欢迎在 PC 社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

AtomGit 仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_SweetHome3D

一、技术架构分析

1.1 整体架构

本项目采用 Electron Web 层 + 鸿蒙 HAP 壳工程的分层架构:

  • Electron Web 层:运行于 ArkWeb 引擎,提供完整的户型数据建模、2D Canvas 平面渲染、Three.js 3D 场景装配、Canvas 2D 等轴测软件渲染、家具目录管理、撤销重做、文件操作等室内设计能力
  • 鸿蒙 HAP 壳工程:通过 web_engine 模块加载 Web 应用,提供窗口管理、文件系统访问、原生对话框、PNG 导出、屏幕自适应等系统能力

这种架构的核心优势在于:业务代码与平台完全解耦,Electron 部分可以在任何平台独立运行,鸿蒙壳仅负责适配和打包。与采用 contextBridge 安全模型的 Electron 应用不同,本工程采用 nodeIntegration:true + contextIsolation:false 的直通模式------渲染进程直接访问 Node.js 的 fs、path、electron 的 ipcRenderer 等 API,主进程负责 dialog 对话框、文件读写与 PNG 导出 IPC,引擎逻辑全部在渲染进程内完成。

1.2 技术方案

对比项 传统室内设计软件(Sweet Home 3D / SketchUp) 鸿蒙适配版(宅匠 3D)
建模引擎 Java + Java3D / C++ + OpenGL 纯 JavaScript 数据模型 + Three.js
3D 渲染 硬件加速 OpenGL WebGL 主渲染 + Canvas 2D 等轴测软件兑底
2D 渲染 Java2D / Cairo Canvas 2D API 参数化绘制
家具库 商业 3D 模型库 24 种预设 + box/cyl/sph/cone 造型件描述
文件操作 专有格式 JSON 序列化 + .sh3d/.json + PNG 导出
部署方式 安装包 HAP 包(鸿蒙应用)
运行环境 Windows/Linux/macOS 鸿蒙 PC(ArkWeb 引擎)

1.3 核心功能清单

功能模块 具体能力
家具预设库 24 种家具(6 分类)+ 3D 造型件描述(box/cyl/sph/cone)
参数化墙体 起点终点定义 + 厚度属性 + 门窗沿墙参数定位(0~1 位置)
2D 平面渲染 Canvas 绘制墙体/门窗/家具/房间/标注/文本 + 选择高亮
3D 装配数据 buildSceneData 由数据模型产出 boxes/cyls/sphs/cones/floors
WebGL 渲染 Three.js 场景 + MeshLambertMaterial + 阴影 + OrbitControls
软件渲染兑底 Canvas 2D 等轴测投影 + 画家算法 + 背面剔除 + 深度排序
交互模式 8 种模式(选择/墙体/门/窗/房间/家具/标注/文本)
撤销重做 快照栈(50 步上限)+ JSON 序列化/反序列化
文件操作 IPC 3 个通道(file:open + file:save + file:exportPng)
弹层系统 grid 同格叠加 + 文本输入弹层 + 关于对话框
鸿蒙安全 菜单移除 + 新窗口拦截 + 禁用硬件加速 + SwiftShader 开关
深色主题 Catppuccin Mocha 配色 + CSS 变量体系

二、环境准备

2.1 开发环境要求

项目 版本/信息
操作系统 Windows 10/11
核心框架 Electron (Node.js + Chromium)
技术栈 HTML5/Vanilla JavaScript
3D 引擎 Three.js(MIT 协议)
渲染引擎 WebGL + Canvas 2D API
目标设备 鸿蒙 PC
目标架构 arm64-v8a
开发工具 DevEco Studio(鸿蒙官方 IDE)

2.2 项目结构

暂时无法在飞书文档外展示此内容

2.3 依赖说明

依赖 版本 用途
electron 系统内置 桌面应用框架
Three.js r120+ 3D WebGL 渲染引擎
OrbitControls 配套 3D 视角轨道控制器
Sweet Home 3D 参考设计 四面板架构与交互设计思想

2.4 应用身份信息

字段 值 说明
bundleName com.wei.zhaijiang3d 应用唯一标识
vendor wei 开发者
versionName 1.0.0 应用版本
应用名称 宅匠 3D 室内设计 中文显示名
英文名 zhaijiang3d package.json name
许可证 Apache-2.0 开源协议

2.5 构建与部署

构建流程遵循鸿蒙 HAP 壳工程的标准步骤:

  1. 在 DevEco Studio 中打开 ohos_hap 工程
  2. 确保 web_engine/src/main/resources/resfile/resources/app/ 部署目录包含所有业务文件
  3. 选择 arm64-v8a 目标架构,构建 HAP 包
  4. 安装到鸿蒙 PC 设备运行

2.6 部署目录同步

每次修改 Electron 层代码后,需将文件同步到部署目录:

暂时无法在飞书文档外展示此内容

2.7 鸿蒙权限配置

module.json5 中根据应用实际需要声明权限。宅匠作为室内设计工具,主要使用文件系统相关权限用于户型图文件存取与 PNG 图片导出。

三、核心适配流程

3.1 主进程:窗口创建与安全配置

主进程负责窗口管理、文件操作 IPC 和 PNG 导出,采用 nodeIntegration:true 直通模式:

暂时无法在飞书文档外展示此内容

关键配置说明:

  • disableHardwareAcceleration:禁用硬件加速,避免鸿蒙 GPU 兼容性问题
  • enable-unsafe-swiftshader:允许软件 WebGL 回退,供 Three.js 在无 GPU 环境创建渲染上下文
  • 菜单双保险:Menu.setApplicationMenu(null) + mainWindow.setMenu(null)
  • 窗口尺寸:主显示器 workAreaSize 的 95% 宽 × 90% 高
  • 新窗口拦截:setWindowOpenHandler 拒绝一切新窗口创建
  • 安全模型:nodeIntegration:true + contextIsolation:false,渲染进程直接访问 Node.js API

3.2 文件操作 IPC:3 个通道

主进程提供 3 个 IPC 通道处理文件操作:

暂时无法在飞书文档外展示此内容

三个通道各有分工:file:open 支持 .sh3d 和 .json 两种扩展名;file:save 默认文件名取"户型图.sh3d";file:exportPng 接收 Canvas dataUrl 并剥离 base64 头部后写入文件。

四、家具预设库与 3D 造型件系统

4.1 家具分类与预设结构

home-engine.js 定义了 6 个家具分类和 24 种预设:

暂时无法在飞书文档外展示此内容

每种预设包含 2D 属性(label/cat/width/height/color/icon)和 3D 造型件描述 d3。d3 数组中每个元素描述一个基本几何体:

暂时无法在飞书文档外展示此内容

4 种几何体类型:box(盒体)、cyl(圆柱)、sph(球体)、cone(圆锥)。每种造型件使用局部坐标(cm,y 向上),pos 为几何中心位置,size 为宽高深。

4.2 家具数据操作

createHomeData 返回包含 7 类元素数组的数据模型:

暂时无法在飞书文档外展示此内容

addFurniture 根据预设类型创建家具实例:

暂时无法在飞书文档外展示此内容

updateFurniture 支持批量属性更新(位置、旋转、尺寸等),通过 Object.keys 遍历 props 逐字段赋值。

4.3 墙体与门窗系统

墙体由起点终点定义,厚度默认 10cm。addWall 创建墙体,removeWall 删除墙体时级联清理依附的门窗:

暂时无法在飞书文档外展示此内容

门窗通过 wallId 关联宿主墙,position 为 0~1 的沿墙参数位置。getWallPoint 函数将参数位置转换为实际坐标:

暂时无法在飞书文档外展示此内容

findNearestWall 使用 pointToSegmentDist 计算点到线段距离,找到鼠标附近最近的墙体及其参数位置,用于门窗放置。

五、2D 平面渲染引擎

5.1 渲染管线

renderFloorPlan 是 2D 渲染入口,按层级顺序绘制所有元素:

暂时无法在飞书文档外展示此内容

9 层渲染顺序确保家具覆盖墙体、门窗嵌入墙体、选择高亮在最上层。

5.2 门与窗的绘制

门绘制为四分之一圆弧(表示开启方向)加一条垂直线段,窗绘制为两条平行线加半透明填充矩形:

暂时无法在飞书文档外展示此内容

门/窗绘制先通过 translate 移动到墙上参数位置,再 rotate 旋转到墙体方向,最后绘制局部图形。

5.3 层级命中检测

hitTest 按优先级检测鼠标命中的元素:家具 > 门 > 窗 > 文本 > 房间 > 墙体。

暂时无法在飞书文档外展示此内容

家具使用矩形命中区域,门/窗使用圆形命中区域(半径 = 宽度/2),文本使用 20px 半径圆形区域,房间使用射线法点包含检测,墙体使用点到线段距离检测。

5.4 房间面积计算

roomArea 使用鞋带公式计算多边形面积,从 cm² 转换为 m²:

暂时无法在飞书文档外展示此内容

pointInPolygon 使用射线法判断点是否在多边形内,用于房间命中检测。

六、3D 渲染引擎:WebGL 与软件双路径

6.1 Three.js 场景初始化

initThree 创建 WebGL 渲染器、透视相机、光照系统和轨道控制器:

暂时无法在飞书文档外展示此内容

detectSoftwareGL 通过 WEBGL_debug_renderer_info 扩展检测渲染器名称,匹配 swiftshader/llvmpipe/software 时降级画质。

6.2 3D 装配数据生成

buildSceneData 将数据模型转换为 3D 几何体描述:

暂时无法在飞书文档外展示此内容

rotateOffset 处理家具旋转后的局部坐标变换:

暂时无法在飞书文档外展示此内容

6.3 WebGL 场景重建

render3D 根据装配数据重建 Three.js 动态组:

暂时无法在飞书文档外展示此内容

matColor 使用 MeshLambertMaterial 替代 MeshStandardMaterial,对光照要求更低,颜色显示更稳定。

6.4 软件渲染兑底

当 WebGL 不可用时,initSoft 创建 Canvas 2D 等轴测渲染兑底:

暂时无法在飞书文档外展示此内容

softProject 实现等轴测投影:世界坐标 (wx, wy, wz) 经旋转、倾斜、缩放后映射到屏幕坐标。depth 值用于画家排序。

renderSoft 按画家算法渲染:先收集所有面的绘制项,按深度远→近排序后依次绘制:

暂时无法在飞书文档外展示此内容

盒体分解为 5 个可见面(底面不可见省略),每面 4 个顶点经旋转后世界坐标即时展开存入数组(不能存闭包,var 被后续迭代覆写会导致全部面画到最后一件家具处)。美术化明暗:顶面最亮 0.96,底面 0.42,侧面按朝向屏幕方向分档。

七、交互模式与编辑工具

7.1 八种绘图模式

setMode 切换当前绘图模式,同时重置临时状态:

暂时无法在飞书文档外展示此内容

7.2 鼠标交互处理

onCanvasMouseDown 根据当前模式分发处理:

暂时无法在飞书文档外展示此内容

7.3 键盘快捷键

bindKeyboardEvents 处理全局键盘事件:

暂时无法在飞书文档外展示此内容

7.4 双视角系统

setViewMode 切换鸟瞰与漫游视角:

暂时无法在飞书文档外展示此内容

鸟瞰模式启用 OrbitControls 自由交互,漫游模式禁用控制器,通过 updateVisitCamera 将相机定位到访客位置和朝向。

八、文件操作与撤销重做

8.1 撤销重做机制

采用快照栈实现,每次修改前推入当前状态序列化快照:

暂时无法在飞书文档外展示此内容

撤销栈上限 50 步,超过则丢弃最早的快照。每次 undo/redo 都通过 afterRestore 统一刷新属性面板、家具列表和 3D 视图。

8.2 JSON 序列化与反序列化

serialize 直接 JSON.stringify,deserialize 包含完整的元素校验和悬空引用清理:

暂时无法在飞书文档外展示此内容

pickValid 过滤结构不完整的元素,validNum 校验数值有效性。悬空引用清理确保门窗的 wallId 指向存在的墙体。nextId 取持久化值与现存最大 id+1 的较大者,避免删除高 id 元素后导入导致 id 复用。

8.3 PNG 导出

exportPlanPng 和 export3dPng 分别导出 2D 平面图和 3D 视图截图:

暂时无法在飞书文档外展示此内容

导出前清除选择高亮,导出后恢复。3D 导出根据当前渲染路径选择 WebGL 或软件渲染 Canvas。

九、常见问题与解决方案

Q1:软件 WebGL 环境下 Three.js 渲染全黑

问题现象: 禁用硬件加速后,Three.js 场景全部渲染为黑色,无法看到任何家具和墙体。

根因分析: 软件 WebGL 实现(SwiftShader/llvmpipe)不支持 PBR 材质(MeshStandardMaterial)所需的光照计算,导致所有表面反射为零。

解决: 改用 MeshLambertMaterial 替代 MeshStandardMaterial,并检测软件渲染环境后关闭阴影和高开销画质项:

暂时无法在飞书文档外展示此内容

MeshLambertMaterial 使用漫反射光照模型,对 GPU 要求极低,软件 WebGL 下颜色显示稳定。detectSoftwareGL 通过 WEBGL_debug_renderer_info 扩展匹配渲染器名称。

Q2:软件渲染兑底时家具面绘制顺序错误导致视觉穿模

问题现象: 软件渲染模式下,家具的某些面穿透其他面显示,视觉上出现穿模现象。

根因分析: 早期软件渲染未实现背面剔除,所有面都参与绘制,导致被遮挡的面仍然可见。

解决: 在收集绘制项时计算面法线与视线方向的点积,大于等于零则为背面,跳过不绘制:

暂时无法在飞书文档外展示此内容

盒体分解为 5 个可见面(底面不可见省略),背面剔除后画家排序不会出现穿模。

Q3:撤销重做栈无限增长导致内存溢出

问题现象: 长时间编辑后应用逐渐变慢,最终内存溢出崩溃。

根因分析: 每次操作都推入快照到撤销栈,未设置上限,大量 JSON 字符串累积导致内存溢出。

解决: 撤销栈设置 50 步上限,超过则丢弃最早的快照:

暂时无法在飞书文档外展示此内容

50 步上限平衡了撤销深度与内存占用,新操作清空重做栈符合用户预期。

Q4:反序列化旧版本文件时渲染崩溃

问题现象: 打开早期版本保存的 JSON 文件时,应用崩溃或渲染异常。

根因分析: 旧版本 JSON 缺少 rooms/dimensions/texts 数组,反序列化后这些字段为 undefined,渲染循环访问 undefined.length 抛异常。

解决: deserialize 入口补齐缺失数组,并对所有元素执行结构校验:

暂时无法在飞书文档外展示此内容

pickValid 过滤结构不完整的元素,悬空引用清理确保门窗的 wallId 指向存在的墙体。

Q5:导出 PNG 时选择高亮残留在图片中

问题现象: 导出的平面图 PNG 中包含选中元素的绿色虚线框。

根因分析: 导出前未清除 selectedId,renderOnce2D 绘制了选择高亮。

解决: 导出前临时保存并清除 selectedId,导出后恢复:

暂时无法在飞书文档外展示此内容

两次 renderOnce2D 分别用于导出无选择图片与恢复界面显示。

Q6:修改了代码,但 HAP 安装后没有生效

问题现象: 修改了 renderer.js、home-engine.js 等文件,重新构建 HAP 安装后,应用行为没有任何变化。

根因分析: 鸿蒙壳工程的实际运行文件从 web_engine/src/main/resources/resfile/resources/app/ 部署目录加载,而非开发目录。修改后必须手动同步。

解决: 每次修改后执行同步命令:

暂时无法在飞书文档外展示此内容

验证方法: 同步后用文件哈希对比确认一致性。

Q7:AGC 审核驳回------requestPermissions 声明了受限 ACL 权限

问题现象: AGC 审核驳回,指出 web_engine/src/main/module.json5 中声明了受限 ACL 权限,但应用隐私政策中未声明对应用途。

根因分析: Electron 壳工程模板默认声明了大量系统权限,宅匠作为室内设计工具不需要位置、相机、麦克风等权限。

解决: 精简权限声明,只保留应用实际使用的系统权限,移除 FILE_ACCESS_PERSIST、READ_WRITE_DESKTOP_DIRECTORY、READ_PASTEBOARD、CAMERA、MICROPHONE 等不需要的权限。宅匠无剪贴板功能,READ_PASTEBOARD 旁路不适用。

十、总结

本文完整记录了在鸿蒙 PC 平台上适配宅匠 3D 室内设计的全过程。通过 Electron 壳方案,我们实现了一个纯 JavaScript 的户型编辑与三维可视化工具,核心特性包括:

特性 实现方案
家具预设库 24 种家具(6 分类)+ box/cyl/sph/cone 3D 造型件描述
参数化墙体 起点终点定义 + 厚度属性 + 门窗沿墙参数定位(0~1 位置)
2D 平面渲染 Canvas 9 层绘制(网格/房间/墙体/门窗/家具/标注/文本/选择)
3D 装配数据 buildSceneData 由数据模型产出 boxes/cyls/sphs/cones/floors
WebGL 渲染 Three.js + MeshLambertMaterial + 阴影 + OrbitControls
软件渲染兑底 Canvas 2D 等轴测投影 + 画家算法 + 背面剔除 + 深度排序
交互模式 8 种模式(选择/墙体/门/窗/房间/家具/标注/文本)
撤销重做 快照栈(50 步上限)+ JSON 序列化/反序列化
文件操作 IPC 3 个通道(open + save + exportPng)支持 .sh3d/.json/.png
弹层系统 grid 同格叠加 + 文本输入弹层 + 关于对话框
鸿蒙安全 菜单移除 + 新窗口拦截 + 禁用硬件加速 + SwiftShader 开关
深色主题 Catppuccin Mocha 配色 + CSS 变量体系

适配过程中最关键的三个点:

  1. Three.js 双渲染引擎------WebGL 主渲染路径使用 MeshLambertMaterial 替代 MeshStandardMaterial 降低 GPU 要求,detectSoftwareGL 检测软件 WebGL 实现后关闭阴影和高开销画质项;Canvas 2D 软件渲染兑底路径实现等轴测投影 + 画家算法 + 背面剔除,盒体分解为 5 个可见面按深度远→近排序绘制
  2. 参数化墙体与门窗系统------墙体由起点终点定义,门窗通过 wallId 关联宿主墙,position 为 0~1 的沿墙参数位置,getWallPoint 将参数位置转换为实际坐标;removeWall 级联清理依附的门窗,findNearestWall 使用 pointToSegmentDist 计算鼠标附近最近墙体
  3. JSON 序列化校验与悬空引用清理------deserialize 入口补齐缺失数组兼容旧版本,pickValid 过滤结构不完整的元素,悬空引用清理确保门窗的 wallId 指向存在的墙体,nextId 取持久化值与现存最大 id+1 的较大者避免 id 复用

这些问题在标准 Electron 环境中不会出现或表现不同,是鸿蒙 ArkWeb 引擎特有的兼容性挑战。通过系统性的排查和修复,最终实现了稳定可用的鸿蒙版纯 JavaScript 户型编辑与三维可视化工具。

相关推荐
行者-全栈开发26 分钟前
【共创稿事节】HarmonyOS 应用上架全流程中的隐私合规:权限最小化、数据本地化与 AppAnalyzer 体检
harmonyos·鸿蒙·隐私合规·应用上架·agc·appanalyzer
Highcharts.js1 小时前
Highcharts Gantt使用符号的自定义数据标签开发甘特图示例
javascript·甘特图·highcharts·图表开发·实战代码·响应式图表
贾伟康2 小时前
HarmonyOS API 26 UKey PIN 授权门禁:状态查询、失败归因与资源闭环
华为·harmonyos
硕迪科技SOLIDWORKS4 小时前
SOLIDWORKS Electrical 2027:从线束定义到2D–3D更改分析
3d·solidworks 202
DolitD4 小时前
UE像素流与云渲染技术方案对比分析
3d·云原生·ue5·云计算·图形渲染
kyriewen4 小时前
Skills 榜前 10 我全装了:只有 3 个能直接用
前端·javascript·程序员
tealcwu5 小时前
【Unity高级】Unity URP:构建同时支持 2D 与 3D 场景的项目完全指南
3d·unity·游戏引擎
工控丫丫5 小时前
帆软看板中经常使用的公式函数及经常搭的组合
前端·javascript·数据库
Dream-Y.ocean8 小时前
鸿蒙PC平台星图遥感适配实战:基于 Electron 壳方案的纯 JavaScript 遥感图像处理引擎开发
javascript·electron·harmonyos