fix: smooth persona hover transitions

Preserve the original reflection material while adding pointer inertia and a short exit hold. Retain the Rive pointer constraint through the fade and blend rapid re-entry to avoid abrupt changes.

Cover timing, re-entry, cancellation, renderer disposal and native Rive frames. The fade regression fails with the previous resource and passes with the updated resource.
This commit is contained in:
2026-10-28 11:46:14 +08:00
parent 4f0f34f460
commit e57e0f099f
12 changed files with 325 additions and 31 deletions
+7 -5
View File
@@ -11,8 +11,9 @@ Agent 保留原版 Obsidian 图形与运动结构,采用经实际动画对比
| [运行时源数据](../design/agent/obsidian-runtime.json) | 从原版解析的 775 个对象,包含图形、属性、关键帧、状态机和绑定数据 |
| [冰水蓝材质参数](../design/agent/obsidian-material.json) | 已确认的水体、暗部、白色高光和水纹颜色,以及球面底色遮蔽度 |
| [离线设计参数](../design/agent/obsidian-offline.json) | 可编辑的播放速度、振幅、混合时长和三层柔光/反射强度 |
| [悬停时间参数](../src/shared/ai-elements/persona-hover-timing.json) | 150ms 渐显、80ms 跟随、100ms 停留、600ms 淡出及 100ms 状态混合 |
| [生成脚本](../scripts/build-agent-persona.mjs) | 验证原版重编码、修改离线动画,生成本地资源 |
| [应用资源](../public/agent/obsidian-water-v1.riv) | 前端加载的 9,879 字节资源 |
| [应用资源](../public/agent/obsidian-water-v1.riv) | 前端加载的 9,929 字节资源 |
| [缓存版本清单](../src/shared/ai-elements/persona-resource-manifest.json) | 生成脚本同步输出的路径、SHA-256 和字节数,用于客户端缓存更新 |
| [静态帧生成脚本](../scripts/render-agent-persona-stills.mjs) | 从实际资源渲染在线、离线 PNG,供首次加载、减少动态效果和失败兜底使用 |
| [资源对照测试](../tests/browser/agent-persona-resource.e2e.ts) | 真实 WebGL2 运行时定时推进,比较原版与修改版画面 |
@@ -64,13 +65,14 @@ JSON 保存原始运行数据;脚本负责可审查的定制。`--check` 验
- 离线循环复用原版三层水纹,时间轴速率从 60 改为 24,约为原速的 40%;这是时间轴播放速度,不是将屏幕渲染限制为 24fps。
- 位移和呼吸振幅缩至原来的 25%;完整旋转保留首尾角度,避免循环接缝。
- 离线循环增加六个柔光/光圈节点的透明度轨道,其中节点 190、199、208 的光圈透明度固定为零;在线循环对应增加原始透明度常量轨道,确保恢复连接后完整恢复。
- 三个循环层根据既有 `asleep` 输入切换,使用 600ms 状态混合。其他输入、状态层和交互保持原定义。
- 三个循环层根据既有 `asleep` 输入切换,使用 600ms 状态混合。
- 悬停保持原版反射几何、渐变与亮度,将 `hover_in` / `hover_out` 分别调整为 9 / 36 帧,即 150ms / 600ms,并使用原有贝塞尔插值器。退出时间轴持续保持节点 129 的指针约束,完全淡出后才释放;进出切换使用 100ms 混合。
生成结果包含 957 个对象、23 段动画。调整设计参数后需再生成并运行对照测试;无需修改前端组件或安装额外依赖。
生成结果包含 960 个对象、23 段动画。调整设计参数后需再生成并运行对照测试;无需修改前端组件或安装额外依赖。
## 前端接入与生命周期
悬停沿用原版节点 110 的反射尺寸,以及节点 129 对指针目标 1 的跟随约束。`Persona` 保留 Rive 监听器,通过外层 Pointer Events 向画布转交鼠标的进入、移动和离开坐标,Rive 自行完成画布与画板坐标转换。画布内部不直接命中鼠标或触摸,只有就绪的外层接收并筛选主指针;不转交触摸和按压,因此手机点击不会触发白色高光。仅手动设置 `hover` 而禁用原生监听,会使目标停在中心,造成中央大片变白。这个问题由事件通路修复,不通过缩小光圈或改变材质补偿。
悬停沿用原版节点 110 的反射尺寸,以及节点 129 对指针目标 1 的跟随约束。`Persona` 保留 Rive 监听器,通过外层 Pointer Events 与 `persona-hover.ts` 向画布转交鼠标的进入、移动和离开坐标;移动坐标以 80ms 时间常数平滑,移出延迟 100ms 后交给资源淡出,Rive 自行完成画布与画板坐标转换。画布内部不直接命中鼠标或触摸,只有就绪的外层接收并筛选主指针;不转交触摸和按压,因此手机点击不会触发白色高光。仅手动设置 `hover` 而禁用原生监听,会使目标停在中心,造成中央大片变白。这个问题由事件通路修复,不通过缩小光圈或改变材质补偿。
`persona-resource.ts` 加载同源的 `/agent/obsidian-water-v1.riv?v=<SHA-256>`。同一页面共用下载 Promise,跨刷新、再次打开页面使用浏览器 Cache Storage 的 `tjwater-agent-persona-v1` 缓存;只共享不可变字节,不共享 Rive 实例或 View Model。
@@ -88,4 +90,4 @@ Agent 服务不可用不妨碍前端读取该资源。这项缓存仅覆盖 `.ri
原始图形来源为 [Surge Obsidian 动画](https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/obsidian-2.0.riv)。解析时参考了 [rive-rs-cli 的格式读取实现](https://github.com/George-RD/rive-rs-cli/blob/main/src/validator/parser.rs)及其从 Rive 定义生成的类型/属性表;应用不依赖或包含该工具。原图形的作者归属不因解析和局部修改而改变。
对照测试在同一 WebGL2 运行时以固定时间步推进。就绪、思考、待回复、输出和悬停状态要求与“仅应用相同冰水蓝材质的原版”采样画面逐像素一致,并验证主色由灰变蓝、合成到白底后仍保有蓝色,球体中心采样区域的不透明度不低于 90%。悬停另外比较左右指针位置对应的高光重心,要求局部反射随指针移动且移出后完全恢复。工作台测试验证实际坐标到达原生监听器。离线检查完整球体、持续变化、初始离线和反复恢复;通过外围像素计数验证光圈完全消失,悬停不会重新出现,恢复在线后再次可见。另用相同运动轨迹、取消离线光照变化的对照资源,验证快速切换后在线明暗完整恢复。该检查覆盖固定采样时刻,不是所有设备、运行时间和状态序列的穷举验证。
对照测试在同一 WebGL2 运行时以固定时间步推进。就绪、思考、待回复、输出和稳定悬停状态要求与“仅应用相同冰水蓝材质的原版”采样画面逐像素一致,并验证主色由灰变蓝、合成到白底后仍保有蓝色,球体中心采样区域的不透明度不低于 90%。悬停另外比较左右指针位置对应的高光重心,要求局部反射随指针移动且移出后完全恢复。时间采样另外验证 150ms 渐显与 600ms 淡出,中途保持可见且最终恢复常态。指针定时测试覆盖惯性跟随、停留、快速进出与卸载清理;工作台测试验证平滑后的实际坐标最终到达原生监听器。离线检查完整球体、持续变化、初始离线和反复恢复;通过外围像素计数验证光圈完全消失,悬停不会重新出现,恢复在线后再次可见。另用相同运动轨迹、取消离线光照变化的对照资源,验证快速切换后在线明暗完整恢复。该检查覆盖固定采样时刻,不是所有设备、运行时间和状态序列的穷举验证。