feat(onboarding): first-chart reveal becomes a star convergence, no buttons (F4)

TASK-birth-sky-followup-20260928 F4:
- pure timeline / seeded scatter / interpolation in lib/birth-sky/converge.ts;
  drawBirthSky takes an optional frame (positions interpolated, alphas
  multiplied) so the settled frame is the exported cover call for call
- lazy full-screen <dialog> overlay driven by requestAnimationFrame at
  viewport x DPR; tap / click / Esc end it; reduced motion only fades
- data, serif and overlay chunk must all arrive inside the 3 s budget,
  otherwise skipped silently; page.tsx wiring unchanged
- one sonner hint after it ends; dialog reveal variant (继续, is-reveal) removed
- DESIGN §17, VOICE, CHANGELOG, checklist, PROGRESS with verification

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
Jesse_Chen
2026-09-28 22:07:01 +08:00
co-authored by Claude Opus 5.5
parent a9a917d50c
commit 731c3b364b
15 changed files with 654 additions and 92 deletions
+10 -5
View File
@@ -1160,9 +1160,14 @@ D40 / D45 / D60 的表格上方,出生时间不是「已校正」时多一句
- **打印 / PDF:** 同一次取数同时画一张 `paper` palette 的图,只在打印时显示;封面独占 A4 第一页(`break-after: page`,高 246mm 居中,恰好放进报告 `@page` 的 186 × 270mm 版心)。不新增打印入口,仍是报告原来的「打印」。
- **取数与等待:** 组件与正文同一次渲染挂载,副作用在正文上屏之后才发请求;两张图都画好才出现。取数、画图任一失败就不显示封面,不留占位、不加等待态,正文不受影响。组件与样式在懒加载 chunk 里(`components/birth-sky/report-sky-cover.tsx`),首页不带。
### 位置三:首次建盘揭幕
### 位置三:首次建盘群星汇聚过场(2026-09-28 追加,TASK-birth-sky-followup-20260928 F4,替换原弹窗)
- 触发只有一处:首页 onboarding 里**自己的**出生地保存成功(`saveOnboardingPlace` 成功之后)。每个账户只发生一次,所以没有「看过」标记、不写库。星盘档案里新建家人、设置里改资料都不触发。
- 保存成功后,onboarding 先照常走到下一步,再去取 `/api/birth-sky?subject=self` 并画图;**3 秒内**画好就在当前页面之上淡入同一个弹层(600ms 透明度,`prefers-reduced-motion` 时直接出现),多一个主按钮「继续」。超时或失败静默跳过:不提示、不重试、不等待。
- 「继续」、「关闭」、Esc、点遮罩都只关掉弹层,下面就是原本的下一步(问候或校正引导),下一步不因它改变。onboarding 保存出生地后本来不会自动进入出生时间校正,所以不存在「关闭后再进入」的排队。0 点数账户的兑换弹窗若同时出现,天空弹层在它之上(原生 `<dialog>` 顶层),关掉后兑换弹窗仍在。
- 这是用户操作之后的结果展示,不是首页加载等待,不违反 §9「首页只揭幕一次」;首页加载揭幕的轨道环逻辑不动。逻辑在 `hooks/use-birth-sky-reveal.ts`(自持状态),`page.tsx` 只接线(`Home()` 的 `useState` / `useRef` 数不变)。
- **触发不变:** 只有一处——首页 onboarding 里**自己的**出生地保存成功(`saveOnboardingPlace` 成功、下一步已设好之后)。每个账户只发生一次,没有「看过」标记、不写库。星盘档案里新建家人、设置里改资料、平时打开首页都不触发。
- **不等待:** 保存成功后 onboarding 照常走到下一步,同时取 `/api/birth-sky?subject=self`、等宋体(最多 1.5 秒)、加载过场代码;三样都在 **3 秒**内到齐才开始放,否则静默跳过:不提示、不重试、不转圈、不骨架(§9)。过场开始时数据已全部就绪。
- **时序(约 3 秒):** ① 汇聚约 1.5 秒:地平线以上的恒星与行星从散开的位置 ease-out(三次方)汇聚到真实投影位置;散开位置由这张盘自己的数据做种子,均匀撒在地平线圆内,同一张盘每次一样。地平线圆、30° / 60° 高度圈、方位字、星座连线、行星名同步淡入。整层前 200ms 淡入,避免页面一下变黑。② 显现文字约 1 秒:顶部小字、细线、主句、事实句、底部说明在前 400ms 淡入,然后停留。③ 淡出约 0.5 秒:整层透明度降到 0,露出**原本的下一步**(问候 / 首页,或出生时间校正引导),下一步不因它改变。
- **最后一帧就是封面:** 每一帧都是 `drawBirthSky` 画的,只对位置插值(t = 0 散开、t = 1 真实位置,`interpolateSkyPoint`,t = 1 精确返回真实点)、对透明度乘系数;停留阶段的画布调用序列与导出封面逐条相同(单测逐调用比对)。
- **全屏、无按钮:** 原生 `<dialog>`(顶层,0 点数账户的兑换弹窗若同时出现,过场在它之上)铺满视口,底色是 `night` 的页底色;画布按视口取最大的 3:4 区域、乘 `devicePixelRatio` 绘制,`requestAnimationFrame` 驱动,卸载时取消。点击、触摸(`pointerdown`)或 Esc 立刻结束,直接进入下一步。
- **减少动态效果:** `prefers-reduced-motion: reduce` 时不汇聚:成品画面整体淡入(400ms)、停留、同一时刻淡出,总长同样约 3 秒。
- **结束后的提示:** 过场正常结束或被点掉后,首页用已有的 sonner Toaster 轻提示一次「这片天空在星盘页可以保存。」(VOICE.md)。取数失败、超时时没有过场,也没有提示。
- **平时的首页加载动画不改。** 这是用户操作之后的一次性结果展示,不是首页加载等待,不违反 §9「首页只揭幕一次」;轨道环揭幕逻辑不动。
- 代码:时序与插值是 `lib/birth-sky/converge.ts` 的纯函数;过场组件 `components/birth-sky/birth-sky-converge.tsx`(懒加载 chunk,含 `draw.ts` 与样式);状态在 `hooks/use-birth-sky-reveal.ts`,`page.tsx` 接线未变(`Home()` 的 `useState` / `useRef` 数不变)。星盘页的「那一刻的天空」弹窗删掉了只给揭幕用的 `is-reveal` 样式与「继续」按钮,其余不动。