From cffe02c1ecc72358dd69221a0569ab2cddc26aa4 Mon Sep 17 00:00:00 2001 From: tianmo Date: Sun, 30 Aug 2026 21:09:18 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=B6=E6=AE=B59=E5=BC=80=E5=8F=91=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/Preset/index.json | 24 +- .../{纯粒子+spine.json => 纯粒子-spine.json} | 0 .../{纯粒子+碰撞.json => 纯粒子-碰撞.json} | 0 .../{纯粒子+路径.json => 纯粒子-路径.json} | 0 src/editor/editorConfig.ts | 2 +- src/views/ParticlePanel.vue | 3 +- 网页粒子系统阶段九.md | 404 ++++++++++++++++++ 7 files changed, 419 insertions(+), 14 deletions(-) rename public/Preset/{纯粒子+spine.json => 纯粒子-spine.json} (100%) rename public/Preset/{纯粒子+碰撞.json => 纯粒子-碰撞.json} (100%) rename public/Preset/{纯粒子+路径.json => 纯粒子-路径.json} (100%) create mode 100644 网页粒子系统阶段九.md diff --git a/public/Preset/index.json b/public/Preset/index.json index 64137f1..3785d38 100644 --- a/public/Preset/index.json +++ b/public/Preset/index.json @@ -1,20 +1,20 @@ [ + { + "name": "纯粒子-路径", + "file": "纯粒子-路径.json" + }, + { + "name": "纯粒子-碰撞", + "file": "纯粒子-碰撞.json" + }, + { + "name": "纯粒子-spine", + "file": "纯粒子-spine.json" + }, { "name": "纯粒子", "file": "纯粒子.json" }, - { - "name": "纯粒子+路径", - "file": "纯粒子+路径.json" - }, - { - "name": "纯粒子+碰撞", - "file": "纯粒子+碰撞.json" - }, - { - "name": "纯粒子+spine", - "file": "纯粒子+spine.json" - }, { "name": "空场景", "file": "空场景.json" diff --git a/public/Preset/纯粒子+spine.json b/public/Preset/纯粒子-spine.json similarity index 100% rename from public/Preset/纯粒子+spine.json rename to public/Preset/纯粒子-spine.json diff --git a/public/Preset/纯粒子+碰撞.json b/public/Preset/纯粒子-碰撞.json similarity index 100% rename from public/Preset/纯粒子+碰撞.json rename to public/Preset/纯粒子-碰撞.json diff --git a/public/Preset/纯粒子+路径.json b/public/Preset/纯粒子-路径.json similarity index 100% rename from public/Preset/纯粒子+路径.json rename to public/Preset/纯粒子-路径.json diff --git a/src/editor/editorConfig.ts b/src/editor/editorConfig.ts index e317f79..2feef0c 100644 --- a/src/editor/editorConfig.ts +++ b/src/editor/editorConfig.ts @@ -223,7 +223,7 @@ export async function applyEditorConfig(store: EditorStore, input: EditorConfigF repairSelection(store, config.selection) store.timeline.frame = 0 store.timeline.recorded = false - store.timeline.playing = false + store.timeline.playing = true for (const system of store.systems) { if (!store.timeline.animations.includes(system.animation)) system.animation = store.timeline.animations[0] } diff --git a/src/views/ParticlePanel.vue b/src/views/ParticlePanel.vue index 09ac278..6931136 100644 --- a/src/views/ParticlePanel.vue +++ b/src/views/ParticlePanel.vue @@ -850,7 +850,8 @@ async function loadSelectedPreset() { configMessage.value = `已加载预设:${file.replace(/\.json$/i, '')}` } catch (error) { configError.value = true - configMessage.value = error instanceof Error ? error.message : String(error) + const message = error instanceof Error ? error.message : String(error) + configMessage.value = `${message},请检查预设文件命名` } finally { presetLoading.value = false selectedPreset.value = '' diff --git a/网页粒子系统阶段九.md b/网页粒子系统阶段九.md new file mode 100644 index 0000000..3ed594b --- /dev/null +++ b/网页粒子系统阶段九.md @@ -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. 增加配置资源大小统计和大文件保存提示。 +