阶段9开发完成

This commit is contained in:
tianmo
2026-08-30 21:09:18 +08:00
parent 94824f0df1
commit cffe02c1ec
7 changed files with 419 additions and 14 deletions
+404
View File
@@ -0,0 +1,404 @@
# 网页粒子系统 · 阶段九
> 阶段定位:**撤回与快捷键、编辑器配置持久化、可复用预设体系定版**
> 完成日期:2026-08-30
> 工程目录:`/Users/tianmokeji/Desktop/SpineParticle`
> 技术栈:Vue 3 + TypeScript + Pinia + PixiJS 7 + Spine Runtime 4.2 + Vite
---
## 一、阶段结论
阶段九在阶段八场景编辑、Spine 骨骼跟随和对象复制能力的基础上,补齐了编辑器日常使用所需的撤回、快捷键、工程保存、工程加载和预设加载流程。
编辑器现在可以把粒子系统、碰撞体、路径、Spine、系统设置、画布视图和动画归属等数据保存为可移植 JSON,并能把 Spine 骨架、Atlas 与多张纹理一并内嵌。配置重新加载后不需要再次选择 Spine 原始文件,画布、动画列表和骨骼树可以直接恢复。
本阶段主要完成:
1. 实现默认 20 步、最大 200 步的撤回历史;
2. 在系统设置中增加最大撤回次数设置;
3. 支持 `Ctrl/Cmd + Z` 撤回;
4. 增加画布工具、时间轴和保存配置快捷键;
5. 实现完整编辑器配置 JSON 下载保存;
6. 实现本地 JSON 配置加载和数据恢复;
7. 将 Spine 原始资源以内嵌 Data URL 形式纳入配置;
8. 修复 Spine 配置恢复后的运行时资源注册和渲染;
9. 实现 `public/Preset/` 预设目录与自动索引;
10. 实现预设列表、空状态和一键加载;
11. 建立纯粒子、路径、碰撞体、Spine 和空场景测试预设;
12. 修复包含 `+` 的预设文件名无法通过开发服务器读取的问题;
13. 为预设加载错误增加文件命名检查提示;
14. 修复同一配置第二次加载后停在第 0 帧的问题;
15. 完成普通配置和含 Spine 配置的保存—加载往返验证。
---
## 二、撤回功能
### 2.1 历史模型
编辑器通过快照记录可撤回数据。默认保留 20 步,用户可以在系统设置中修改,允许范围为 1~200 步。
快照覆盖:
- 全部粒子系统及其面板参数;
- 全部碰撞体;
- 全部路径与路径点;
- 全部 Spine 场景对象;
- 所属动画列表;
- 除撤回上限自身以外的系统设置;
- 对象显隐、背景图数据及背景偏移和透明度。
以下内容不作为普通撤回快照保存:
- 粒子逐帧录制缓存;
- PixiJS `Texture` 实例;
- 拖尾运行时纹理实例;
- 当前时间轴帧和正在播放状态;
- 撤回上限本身。
这些数据属于运行时缓存或撤回系统自身配置,恢复快照后由编辑器重新计算或重建。
### 2.2 操作合并
参数连续拖动或连续输入不会为每个微小变化创建独立历史。编辑器在最后一次变化后等待约 250ms,再把连续变化合并为一步操作。
该设计适用于:
- 滑块拖动;
- 数值连续输入;
- 曲线调整;
- 场景对象变换;
- 背景图偏移和透明度调整。
### 2.3 Spine 资源安全
Spine 场景对象的可序列化属性保存在快照中,已解析的 SkeletonData 和纹理通过资源注册表引用计数保留。
撤回删除、复制或资源替换操作时:
- 快照仍引用的 Spine 资源不会被提前销毁;
- 恢复快照时重新注册对应 Spine ID 的运行时资源;
- 超出撤回上限的旧快照会释放自己持有的资源引用;
- 粒子录制缓存清空并重新生成。
### 2.4 撤回后的状态修复
执行撤回后会:
1. 恢复场景对象和设置;
2. 修复当前选择,避免选择已不存在的对象;
3. 恢复粒子系统所属动画;
4. 清空旧录制帧;
5. 重新计算时间轴总长度;
6. 更新撤回按钮的剩余步数和可用状态。
当前阶段只实现“撤回”,尚未实现“重做”。
---
## 三、快捷键
### 3.1 快捷键表
| 快捷键 | 作用 | 生效范围 |
|---|---|---|
| `Ctrl/Cmd + Z` | 撤回上一步 | 全局 |
| `Ctrl/Cmd + S` | 下载保存当前编辑器配置 | 全局 |
| `Q` | 切换为画布拖动模式 | 画布已选中 |
| `W` | 切换位移工具 | 画布已选中 |
| `E` | 切换旋转工具 | 画布已选中 |
| `R` | 切换缩放工具 | 画布已选中 |
| `D` | 播放/暂停时间轴 | 全局非输入状态 |
| `S` | 回到时间轴第 0 帧 | 全局非输入状态 |
### 3.2 浏览器快捷键覆盖
`Ctrl/Cmd + S``Ctrl/Cmd + Z` 会阻止浏览器默认行为,分别交给编辑器保存和撤回,不再弹出浏览器网页保存或执行浏览器输入历史撤销。
### 3.3 输入控件保护
当焦点位于以下控件时,普通字母快捷键不会触发:
- `input`
- `textarea`
- `select`
- `contenteditable` 内容。
因此输入名称、搜索骨骼或编辑参数时,可以正常键入 `q/w/e/r/d/s`。带 `Ctrl/Cmd` 的保存与撤回仍按编辑器全局快捷键处理。
---
## 四、编辑器配置保存
### 4.1 保存入口
场景对象面板上方增加:
- `保存配置`
- `加载配置`
- `选择预设`
点击“保存配置”或按 `Ctrl/Cmd + S`,浏览器会下载:
```text
SpineParticle-YYYYMMDD-HHMMSS.json
```
### 4.2 文件格式
当前格式标识与版本:
```text
format: SpineParticle.EditorConfig
version: 1
```
顶层数据结构:
```text
editor 画布缩放与平移
settings 系统设置、背景图及撤回上限
timeline FPS、循环状态和所属动画列表
selection 当前场景对象类型与 ID
scene 粒子、碰撞体、路径和 Spine 数组
savedAt 保存时间
```
### 4.3 粒子与图片资源
粒子系统保存全部可编辑参数,包括:
- 发射模式、持续时间和延迟;
- 发射器形状和根变换;
- 生命周期、速度、缩放、旋转与曲线;
- 颜色、透明度、混合模式;
- 修改器;
- 碰撞、路径和骨骼跟随;
- 图片资源与拖尾资源;
- 对象显隐和所属动画。
PixiJS 纹理实例和粒子逐帧缓存不会写入 JSON。图片资源通过自身的预览 URL 或 Data URL 保存,配置加载时重新创建纹理。
### 4.4 Spine 原始资源内嵌
Spine 对象除属性、动画选择和骨骼元数据外,还保存原始资源副本:
```text
sourceBundle.skeleton
sourceBundle.atlas
sourceBundle.textures[]
```
每个资源同时保存原文件名和 Data URL。恢复时重新创建同名 `File`
- JSON 使用原 JSON 文件名;
- SKEL 使用原二进制文件名;
- Atlas 保留原 Atlas 文件名和内容;
- 纹理保留 Atlas 中引用的准确文件名。
保持文件名一致是 Atlas 再次匹配多张纹理的关键。Data URL 只是文件内容载体,不会替代资源之间的文件名引用关系。
Spine 面板在首次加载成功后异步生成 `sourceBundle`,避免内嵌资源转换干扰当前画布中的实时 Spine 解析结果。
---
## 五、编辑器配置加载
### 5.1 校验
加载本地文件时只接受 `.json`,并依次检查:
1. 是否为有效 JSON
2. `format` 是否为 `SpineParticle.EditorConfig`
3. 配置版本是否为 1
4. 场景是否包含粒子、碰撞体、路径和 Spine 数组。
失败时保持当前编辑器状态,并显示具体错误。
### 5.2 原子恢复流程
配置加载先在临时数据中完成资源准备:
1. 深度复制配置数据;
2. 补齐旧粒子参数默认值;
3. 重建粒子和拖尾纹理;
4. 还原 Spine 原始文件;
5. 解析骨架、Atlas 和多纹理;
6. 全部解析成功后再替换当前场景;
7. 注册新的 Spine 运行时资源;
8. 修复对象选择和所属动画;
9. 重新计算时间轴长度。
如果内嵌 Spine 资源解析失败,已临时创建的纹理会释放,不会把半完成场景覆盖到编辑器中。
### 5.3 Spine 运行时刷新
重复加载配置时,Spine 对象 ID 可能相同。加载流程会提升 `assetVersion`,确保运行时层识别为新资源并重新创建 Spine 显示对象,而不是继续使用旧实例。
资源加载完成后会恢复:
- Spine 画布渲染;
- 动画列表和动画时长;
- 当前选择的 Spine 动画;
- 骨骼层级树;
- 当前选中骨骼;
- 粒子系统保存的 Spine ID 与骨骼名称绑定。
### 5.4 加载后自动播放
配置和预设加载完成后统一执行:
```text
frame = 0
recorded = false
playing = true
```
因此首次加载和连续加载同一份配置都会从头自动播放,不再依赖场景深度监听是否检测到数据变化。
---
## 六、预设体系
### 6.1 预设目录
预设文件放置于:
```text
public/Preset/
```
每个预设都是与“保存配置”完全相同的 `SpineParticle.EditorConfig` JSON,因此预设和本地工程配置共用一套解析、校验和资源恢复流程。
### 6.2 自动索引
Vite 插件会扫描预设目录中的全部 `.json` 文件并自动生成:
```text
public/Preset/index.json
```
索引生成时:
- 排除 `index.json` 自身;
- 按中文文件名排序;
- 使用文件名去掉 `.json` 作为界面名称;
- 开发服务器监听文件新增和删除并刷新列表;
- 生产构建开始前重新生成索引。
目录中没有配置文件时,下拉框显示“无预设文件”并置灰。
### 6.3 当前预设
当前项目包含:
- `纯粒子.json`
- `纯粒子-路径.json`
- `纯粒子-碰撞.json`
- `纯粒子-spine.json`
- `空场景.json`
- `默认粒子.json`
带 Spine 的预设内嵌 `bingo.json``bingo.atlas``bingo.png``bingo_2.png`,加载后可直接显示 Spine、动画列表和骨骼树。
### 6.4 文件命名规则
阶段测试发现,文件名包含 `+` 时,经 URL 编码后开发服务器会返回应用 HTML,而不是目标 JSON,最终表现为“配置文件不是有效的 JSON”。
为确保开发环境和静态托管环境行为一致,组合预设统一使用连字符:
```text
推荐:纯粒子-路径.json
避免:纯粒子+路径.json
```
预设加载失败时,界面在原始错误后追加:
```text
请检查预设文件命名
```
---
## 七、代码结构
本阶段新增或重点调整:
| 文件 | 职责 |
|---|---|
| `src/editor/editorClone.ts` | 安全深度复制编辑器可序列化数据 |
| `src/editor/editorHistory.ts` | 撤回快照、合并、上限和 Spine 资源引用 |
| `src/editor/useEditorShortcuts.ts` | 全局快捷键、画布焦点和输入保护 |
| `src/editor/editorConfig.ts` | 配置创建、下载、校验、加载和资源恢复 |
| `src/spine/spineSourceBundle.ts` | Spine 原始文件 Data URL 内嵌与还原 |
| `src/spine/spineAssetRegistry.ts` | Spine 配置恢复和撤回所需的资源生命周期 |
| `src/views/ParticlePanel.vue` | 保存、加载、预设列表和结果提示 |
| `src/views/Stage.vue` | 快捷键接入、撤回按钮和加载后的运行时刷新 |
| `vite.config.ts` | 预设索引自动生成和 Spine 依赖去重 |
配置、历史与快捷键逻辑已从主画布和主属性面板中拆分,避免继续扩大单文件职责。
---
## 八、验证结果
已完成以下实际验证:
- `Ctrl/Cmd + S` 可以下载编辑器配置;
- 配置文件名按时间生成且 JSON 格式有效;
- 修改粒子名称后保存,再次修改并加载,名称恢复为保存值;
- 系统设置、背景图数据、画布视图和场景对象写入配置;
- 含 Spine 的配置保存后文件约 11MB,资源数据完整内嵌;
- 含 Spine 配置重新加载后,Spine 名称恢复;
- Spine 画布图像正常显示;
- 14 个 Spine 动画及“无”选项正常恢复;
- 骨骼层级树正常恢复;
- 页面未出现资源解析或渲染错误;
- `纯粒子.json` 加载成功;
- `纯粒子-路径.json` 加载成功,场景包含粒子与路径;
- `纯粒子-碰撞.json` 加载成功,场景包含粒子与碰撞体;
- `纯粒子-spine.json` 加载成功,场景包含粒子与 Spine;
- `空场景.json` 可以加载为零对象场景;
- `默认粒子.json` 加载成功;
- 同一 Spine 预设连续加载两次,两次都从第 0 帧自动播放;
- 第二次加载后帧数从 40 推进到 59,播放按钮保持“暂停”状态;
- 浏览器控制台无错误和警告;
- `npm run build` 生产构建通过。
构建仍存在 Vite 主产物超过 500kB 的提示,不影响功能和构建结果。
---
## 九、当前限制
1. 当前只支持单向撤回,尚未实现重做;
2. 撤回历史只存在于当前页面内存中,刷新页面后清空;
3. 撤回不保存当前播放帧、播放状态、画布选择和粒子录制缓存;
4. 配置格式当前为版本 1,尚未建立跨版本迁移器;
5. Spine 和本地图片使用 Data URL 内嵌,资源较大时配置文件体积明显增加;
6. 保存配置始终下载新文件,尚未提供覆盖保存、最近文件和自动保存;
7. 浏览器安全限制下无法记住原本地文件路径;
8. 预设依赖构建时生成的索引,纯静态部署后新增文件需要重新构建;
9. 预设命名目前通过规避 `+` 保证兼容,尚未建立完整的文件名字符校验;
10. 配置导入目前只接受 JSON,尚未支持 YAML;
11. 配置文件没有压缩,包含 Spine 多纹理时下载和解析成本较高;
12. 主包仍包含 Spine 运行时和主要编辑器模块,尚未动态拆包。
---
## 十、阶段十建议
1. 增加重做功能和 `Ctrl/Cmd + Shift + Z` 快捷键;
2. 增加配置格式迁移器,支持未来版本平滑升级;
3. 为预设文件名建立创建、校验和错误定位工具;
4. 提供自动保存、最近配置和页面异常恢复;
5. 支持压缩工程包,把 JSON 与 Spine、粒子图片资源拆分存储;
6. 增加配置导入前预览、差异对比和合并模式;
7. 为配置与预设建立自动化往返测试;
8. 对无效 ID、丢失骨骼、缺失纹理和旧字段提供迁移报告;
9. 增加工程名称、作者、备注和自定义元数据;
10. 开始设计正式导出结构,把所属动画、粒子系统和 Spine 骨骼绑定映射到导出数据;
11. 对 Spine 和编辑器主模块进行动态加载,降低初始包体积;
12. 增加配置资源大小统计和大文件保存提示。