Files
SpineWallpaper/.scratch/wallpaper-optimization/issues/05-we-properties.md
T
Shuery 3f11426964 feat: TS 构建管线、Spine 抓取器与 wallpapers/ 唯一真相来源
把项目从「手写 dist/」改成「wallpapers/ 是唯一真相来源,dist/ 由 pnpm build 生成」,
并补上配套的类型、门禁与抓取器。一次提交落地整条管线,因为拆开会留下不能构建的中间态。

- src/:运行时与模拟器源码(TS,strict),编译到 build/ 再拷进各分发
- tools/:build / dev / check-{syntax,paths,dist},以及抓取器与回归门禁 tools/checks/
  (.scratch/ 下那批一次性脚本移入 tools/checks/ 并入库为长期门禁)
- wallpapers/:七档壁纸的源数据 + README.md(id/音频/预设的完整规范)
- docs/adr/0005-0008:构建管线与分发拓扑、模拟器契约、自包含 sim、每骨架资源布局
- .gitignore:排除 .scratch/ 的参考资料副本(上游 spine 整仓克隆 ~1.2 GB、
  抓取侦查数据 ~680 MB)与调试转储;这些是本地调查材料,补偿会让仓库无法克隆
- 归一化 .gitignore/CONTEXT.md 行尾(工作区 CRLF、索引 LF 造成的整文件假 diff)

同时修掉三档卡住构建的未完工壁纸:
- kv45 的 meta.json 里 id 还是抓取期场景名 scene_main,经 downloader promote 正名为 kv45
- shajin / zhigengniao_juheye 的 meta.json 误用了骨架描述文件(name/spine/animations/pages)、
  且都缺 preset.template.json;现按规范重建:骨架沉到 spines/<名>/(spine-ts 按 atlas 所在
  目录解析贴图页)、补上元数据与单骨架预设,并清掉 zhigengniao 骨架里指向作者机的绝对路径
- 顺带 promote 已在 sources.yml 里的 kv46(月升之前,与兽共舞)

pnpm check 五道门全绿:10 个分发 / 7 档壁纸 / 141 处引用自包含。
2026-10-02 01:27:02 +08:00

41 lines
2.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 05 WE 属性扩展:音源 combo、fps 限制
Status: resolved
## 内容
1. **音源**:`project.json` 新增 combo `bgm`,列表是所有壁纸音源声明的**并集**
(WE 的 combo 是静态列表,只能手工同步),另加一档「随预设」`auto`:
`auto` / `zaiduheni`「「再度和你」」/ `xilian`「昔涟」/ `pv37`「版本 PV」。
每档壁纸仍在自己 `preset.js` 的 `audioChoices` 里声明可选音源。
2. **fps 限制**:`properties.fps` 经 `applyGeneralProperties` 下发,WE 不会替壁纸限流,自行实现。
3. `setPaused` 不实现(WE 会整体冻结进程)。
## 实现要点
- **音源优先级**:用户自填 URL(`audio_file`)> `bgm` 选中的音源 > 本壁纸默认音源。
所选音源**只在本壁纸声明过时才生效**;不属于当前壁纸则音频回落本壁纸默认,
**选择本身保留**,切回那份壁纸时用户的选择还在。
- **门控绘制**:包裹实例上的 `drawFrame`。播放器循环写作
`requestAnimationFrame(() => this.drawFrame())`——箭头闭包在调用时才读 `this.drawFrame`,
所以在实例上换掉它就能接管排程。
- 原实现只在**真正绘制**时才排下一帧,而被限流丢掉的帧同样要排,否则循环停摆;
因此排程由包装层负责,再让原实现以 `requestNextFrame = false` 调用。
- **踩到的坑**:朴素地"距上一帧不足 `interval` 就丢"会把 rAF 抖动放大成整拍丢失——
目标 30fps 时落在 33.2ms 的那一帧被判为不足 33.33ms 而丢掉,实测只有 **22.5fps**。
改为"留 4ms 容差 + 到期时间按固定步长推进"后精确到 30.0。
- **另一个坑**:`if (properties.fps)` 的真值判断会把 `fps: 0`(取消限流)整个吞掉,
改判 `!== undefined`。
## 验收
`node tools/checks/test-props.mjs`:
| 项目 | 结果 |
| --- | --- |
| bgm 切音源即时生效 | ✓(10 项音源断言全过) |
| 跨壁纸回落 + 选择保留 | ✓ xilian 选「昔涟」→ 切 kv37 回落 pv37 → 切回 xilian 回落默认 |
| 自定义 URL 优先级最高且不被切壁纸清掉 | ✓ |
| 限 30 / 20 / 取消限流 | 实测 **30.0 / 20.0 / 60.0 fps** |
| 不影响动画相位推进速度 | 0.999 / 0.999 / 1.000 倍速(基准 1.000) |