1实现原理 · 为什么它能做到
它管的是「任何转场都能正确合成」的前置条件,而不是某个具体转场——具体转场目录在 transition registry,这份只做底下那层教义(crossfade / push-slide / zoom-through / cut-the-curve 等的目录在别处)。
This is the **render-correctness doctrine** for PLV scene-to-scene seams: the prerequisites and master-timeline mechanics that make any transition composite correctly, independent of which specific transition is chosen. The per-transition catalog (crossfade, push-slide, zoom-through, cut-the-curve, …) lives in the transition registry — this page is the doctrine that sits underneath all of them.
白闪守卫(核心正确性问题):有些转场会开出一个两 wrapper 不透明度之和 <1 的窗口,若 #root 没有不透明底色,渲染器会把这个「凹坑」合成到默认白页上 → 每个接缝一道白闪(暗片上尤其刺眼;作者记录在两次 Spotify 片子上观察到过)。
Several templates open a window where the two wrappers' summed opacity < 1 (the cut-the-curve mid-window cut, zoom-through's 0.15 floor, plain crossfade's power-curve dip). Whatever is BEHIND the wrappers shows through during that window. If the assembled `index.html` `#root` has no opaque background, the renderer composites the dip over its default **white** page → a white flash at every seam, glaring on dark films (observed on two Spotify runs before the fix).
修法是硬性前置:给 #root 刷不透明底色,由 assemble-index.mjs 负责输出;其他消费这些模板的人自担同等保证。
**The assembler must paint the stage:** `#root { background: var(--canvas-deep, var(--canvas, #000)) }` — `assemble-index.mjs` now emits this; any other consumer of these templates owns the same guarantee.
注入器施加转场的四步机序:延长出场 wrapper 的 data-duration(冻住末帧)→ 提前入场 wrapper 的 data-start(造重叠窗)→ 把全部 clip 的 data-track-index 重排为 0/1 乒乓(同轨重叠非法,高轨压在上面)→ 在 T=重叠起点把 gsap_template 打进 window.__timelines["main"]。
1. Extends `#el-<from>` wrapper `data-duration` by `duration_s` (holds its final frame — verified: `core/src/runtime/init.ts:1393-1410` external-slot branch). 2. Pulls `#el-<to>` wrapper `data-start` earlier by `duration_s` (creates the overlap window). 3. Reassigns **all** clip `data-track-index` as a 0/1 ping-pong so the two overlapping wrappers never share a track (same-track overlap is illegal — `core/src/lint/rules/composition.ts`). Higher track composites on top. 4. Stamps the `gsap_template` into `window.__timelines["main"]` at `T = overlap-start`.
适用范围被划定:只覆盖 Tier-B-ready(对两个场景 wrapper 做纯 transform/opacity/filter,不注入 overlay DOM、不需场景配合)的转场;overlay 族(staggered blocks、blinds、light leak、grid dissolve、page burn)与 shader 转场明确推迟到后续阶段。
The transitions this doctrine governs are **Tier-B-ready**: pure transform / opacity / filter on the two scene **clip wrappers** (`#el-<sid>`), no injected overlay DOM, no per-scene cooperation. Overlay families (staggered blocks, blinds, light leak, grid dissolve, page burn) and shader transitions are deferred to later phases.
模板占位符契约:__OLD__ / __NEW__(两个 wrapper 选择器,带引号)、__T__(主时钟上的重叠起点)、__DUR__(该边界的 duration_s)、__DX__ / __DY__(方向位移 ±1920 / ±1080)、__ORIGIN_OUT__ / __ORIGIN_IN__(squeeze 的 transformOrigin 对)。
| `__OLD__` | `"#el-<from>"` — outgoing clip wrapper selector (quoted) | | `__NEW__` | `"#el-<to>"` — incoming clip wrapper selector (quoted) | | `__T__` | overlap-start time in seconds (master clock) | | `__DUR__` | `duration_s` for this boundary |
一个反直觉结论:filter / scaleX / transformOrigin 在主时间线上是 lint-clean 的——因为属性白名单只约束 scene-worker 的 prompt,不约束 index.html。
`filter` / `scaleX` / `transformOrigin` are lint-clean on the master timeline (verified: `core/src/lint/rules/gsap.ts` has no per-property whitelist and scopes its checks to `data-composition-id` ranges; the x/y/scale/rotation/opacity whitelist is a _scene-worker_ prompt rule only — it does not bind index.html).
结论附了验证姿态与日期:由原型渲染实测(2026-05-31)确认主时间线 wrapper tween 被 seek 并渲染(与子 comp 自己的暂停时间线不双 seek,运行时独立驱动)、延长后的 wrapper 冻住场景 i 的末帧、高轨入场 wrapper 与出场 wrapper 叠合并混合。
Verified by prototype render (2026-05-31): the master-timeline wrapper tween is seeked and rendered (no double-seek with the sub-comp's own paused timeline — the runtime drives them independently), the extended wrapper holds scene _i_'s final frame, and the higher-track incoming wrapper composites over + blends with the outgoing one.
加载时机由 description 写明:装配主时间线/index.html 时、出现白闪(尤其暗片)时、要解释为什么转场的不透明度凹坑会透出来时、以及要核对重叠场景 wrapper 的渲染侧机制时。
Load when assembling the master timeline / index.html, when a white flash appears at a cut or crossfade seam (especially on dark films), when reasoning about why a transition opacity dip shows through, or when verifying the render-side mechanics of how overlapping scene wrappers blend.
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| package | 同仓组件依赖:assemble-index.mjs(负责给 #root 刷底)、injector / transition registry(gsap_template 的施加方) |
| reference | 框架源码坐标(作为论证依据被引用):core/src/runtime/init.ts、core/src/lint/rules/composition.ts、core/src/lint/rules/gsap.ts |
4风险提醒 风险提醒:绿色 · 放心使用
- 引用的源码坐标在本 pin 已失效(core/ 前缀与 lint/rules 目录均不存在) — `core/src/runtime/init.ts:1393-1410`、`core/src/lint/rules/{composition,gsap}.ts` 三处路径都查不到(实际为 packages/core/src/…,且 lint 规则已并入 index.ts)。按文档按图索骥会扑空;若侦查不回源就会把失效坐标当「已核事实」引用。
- 文档给出的 CSS 表达式与代码实现不一致 — 文档写 `var(--canvas-deep, var(--canvas, #000))`,但 pin 内 `canvas-deep` 零命中;assemble-index.mjs 实际输出 frame.md 的 canvas 色 + html/body #000 兜底。照抄文档变量名会得到未定义变量(回落 --canvas 再回落 #000,通常仍能用,但可能与真实配色不同)。
- 只覆盖 Tier-B 转场,overlay/shader 族无保证 — 文档明确 deferred to later phases;若用到 staggered blocks / blinds / light leak / shader 转场,本教义不适用,缺前置保护仍可能白闪。
- 教义无 lint 落地,不读文档的代理不会被拦 — 白闪守卫是「装配者必须刷底」的人工纪律,仓库内没有针对它的自动检查(文档也没声称有)。
- 文档自述的行号引用(init.ts:1393-1410)不可复现 — 路径已漂移,行号更无从对上;任何依赖该行号做审阅的流程都会失败。
5第二遍独立确认
- [discrepancy] 文档引用的源码路径在本 pin 是否真实存在(core/src/runtime/init.ts:1393-1410、core/src/lint/rules/composition.ts、core/src/lint/rules/gsap.ts) — 三处在仓库根下均**不存在**(core/src/… 无此目录)。实际已迁到 packages/ 前缀:`packages/core/src/runtime/init.ts` 存在(1393 行附近确为时长/clip 解析相关代码,可见 derivedDuration 等分支,但行号与分支名不能逐字对应 `external-slot branch`);而 `packages/core/src/lint/rules/` 目录不存在,lint 现为 `packages/core/src/lint/index.ts` 单入口。结论:这三条源码坐标是写作时点的旧路径,在本 pin 已失效——不影响其结论(白闪机制、同轨非法),但照抄坐标去查源码会扑空。属上游文档与代码结构漂移,非本 skill 的行为风险。
- [discrepancy] 「assemble-index.mjs now emits this」是否属实,且写法是否与文档一致 — 属实但表达不同。pin 内 assemble-index.mjs 共 4 份(product-launch-video / faceless-explainer / music-to-video / pr-to-video)。以 product-launch-video 版为准:它确实在 <head> 样式里输出 `#root { position: relative; … background: ${groundColor}; }`(groundColor 取自 frame.md 的 canvas 语义色),并在 html,body 上兜底 `background: #000`。而文档写的字面式样是 `var(--canvas-deep, var(--canvas, #000))`——全仓(*.ts/*.mjs/*.html)对 `canvas-deep` **零命中**,即该 CSS 变量名在当前代码里并不存在。行为目标(#root 不透明、不会白闪)一致,具体表达式已演进。
- [ok] 「白闪」结论是否只是传言 — 文档给出机理(summed opacity < 1 的窗口 + 渲染器默认白页)与经验出处(observed on two Spotify runs before the fix),并指认三个会开窗的模板(cut-the-curve 中段、zoom-through 的 0.15 下限、普通 crossfade 的 power 曲线凹坑);后在 cut-the-curve/SKILL.md 里交叉印证(zoom-through 出场透明度确为 0.15)。机理自洽、可验,接受为文档声明。
- [ok] 是否真的没有脚本/网络(green 档前提) — 目录全量仅 SKILL.md;全文 URL、curl、npx、node 调用、process.env 均零命中。文中出现的路径串(core/src/…)是文字引用,不是可执行指令。
- [ok] 「不含 per-transition catalog」的自述是否属实 — SKILL.md 正文只谈前置条件与机序,未列任何具体转场的参数(唯一出现 cut-the-curve/zoom-through 之处是在解释「谁会开出不透明度和 <1 的窗」);转场目录确实不在本目录内。
6结论
b3f716c34090b9a1…d2f0bc7f34