Files
SpineWallpaper/.scratch/build-pipeline/issues/14-panel-animations.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

4.0 KiB
Raw Blame History

14 — 面板动效

Status: resolved Type: task

需求

给这个壁纸设置添加一些细腻的动画吧

一条原则

只做解释状态变化的动画——东西从哪来、去哪了、刚才是哪个变了。

面板是常驻 UI。装饰性的循环动画(呼吸、闪烁、渐变流动)只会让人分心,而且这个面板 每改一个属性就会重画一次,任何"一直在跑"的东西都会变成噪音。所以整套动效里 没有一条 @keyframes 是持续运行的。

加了什么

位置 动效 它在解释什么
抽屉 曲线改成 cubic-bezier(.22,.61,.36,1) 纯 ease 结尾会急停
面板刚滑出 逐行错开上移淡入(--i × 16ms) 这些东西是"刚出来"的
条件新出现的行 同样的入场,不错开 它是被勾出来的
行悬停 底色高亮 + 图标放大 1.16 指针现在在哪一行
复选框 自绘,勾靠 background-size 从 0 长出来 刚才是哪个开关变了
下拉箭头 展开时翻 180° 列表开着
下拉/取色器弹层 缩放 + 上移出现 它从按钮那儿长出来
色块 悬停放大;确认后弹一下 颜色变了但位置没变,不弹容易看不出
重置 悬停时图标转半圈 这是"复位"
确认/取消 悬停提亮、按下下沉 1px 按到了
右边缘把手 悬停淡入并左移 2px 这里能滑出东西

两个容易做错的地方

入场不能每次重画都重播

paintProps() 是 host.textContent = "" 然后整块重建。如果入场动画挂在 #wesim[data-open="1"] .row 这种选择器上,那么每改一个属性——拖一下音量、勾一个开关—— 整个面板都会重播一遍动画,看起来像闪屏。

所以:

  • 刚滑出那一下:给根节点打 data-enter="1",520ms 后摘掉。带令牌,连着开关几次 只有最后一次的定时器能摘。
  • 条件新出现的行:paintProps 记着上一次可见的属性键,只有新冒出来的才加 .row-enter。

那 4 个颜色滑杆是个例外:它们不是属性表里的键(是 WE 内置的),走不到按 key 认新的那套, 所以单独用 colorOptionsShown 判一次"上一次它们不在"。

复选框不能用 ::after 画勾

<input> 是替换元素,伪元素在它上面不生效。所以勾是一张内联 SVG 背景图, 靠 background-size: 0 0 → 11px 11px 长出来(background-size 是可过渡属性)。

prefers-reduced-motion

整套动效在 @media (prefers-reduced-motion: reduce) 里被关掉。这不是可选项: 前庭功能敏感的人会因为界面动而难受,而这套动效对可用性没有任何贡献。

验证

tools/checks/verify-panel.mts 第 ⑩ 组,全部通过:

  • 滑出后 data-enter="1"、行上跑的是 wesim-row-in、--i 是 0,1,2,3,4
  • 播完 data-enter 被摘掉(不会每次重画都重播)
  • 勾「显示颜色选项」后,只有亮度/对比度/饱和度/色调偏移四行带 .row-enter
  • 复选框 appearance: none(确认是自绘的)
  • 展开下拉 → aria-expanded="true" 且箭头 matrix(-1,0,0,-1,0,0)(翻过来了);Esc 后复位
  • Emulation.setEmulatedMedia 模拟 reduce → 动画与过渡时长都降到 ~0

测出来的三个真问题

  1. 颜色滑杆走不到 isNew(key) 那套,永远不带入场标记(上面已说)。
  2. 点开下拉时先把 aria-expanded 设成 true,紧接着 openComboFor 内部的 closeCombo 又把它清掉 —— 顺序反了,箭头永远不翻。改成 openComboFor 之后再设。
  3. 测试自身:动效那一段先移到远处再移到边缘。前面点过「取消」会把 armed 置 false, 而它只有"指针离开边缘"才复位——不先走开一次,合成指针根本打不开面板。

回归

全绿:pnpm check、调试服与浏览器冒烟、热更新、四档自包含包 file://、静态托管预览、 发布产物里动效代码 0 处(wesim-row-in 计数为 0)、6 组渲染与重构前逐像素相同。