首页
/ HyperFrames Motion Doctrine:向量法则、影片电流与 Seam Gate——把多场景视频变成"一次连续的镜头运动"

HyperFrames Motion Doctrine:向量法则、影片电流与 Seam Gate——把多场景视频变成"一次连续的镜头运动"

2026-09-05 16:09:40作者:江焘钦

HyperFrames(Write HTML. Render video. Built for agents.)的仓库内建了一个面向动画创作的"技能(skill)"体系,其中 motion-doctrine 是最高层级的运动学总纲(GATEWAY):它规定任何多场景视频在每个接缝(seam)处"必须发生什么"、每个场景"如何表演",而具体参数与代码模板则下放到 cut-the-curveoversized-cursorseam-craft 等实现层技能。读完本篇,你将掌握 Vector Law(含 Z 轴 scale 符号规则)、The Current 向量预算、ledger.json 向量台账的写法,以及用 seam-stamp.mjs 生成接缝代码、用 seam-gate.mjs 数值化验收的完整工程化工作流。

这套规则针对的失败模式非常具体:各场景孤立创作,导致"观众眼睛的动量在每次剪辑处死掉",场景在入场与退场之间原地晃动(idle wobble)。Doctrine 的定位是决策层——它"决定每个接缝上发生什么",并明确声明其规则覆盖(supersede)通用/上游的运动指导

一、Gateway 定位与路由图

motion-doctrine 是一个 GATEWAY 技能:在编排任何 HyperFrames 动画之前先加载它,它再把你路由到负责实现的具体技能。原文给出的路由表如下:

决策(本技能负责) 实现技能(负责落地)
接缝过渡的选择 + 参数 + 代码 cut-the-curve §1–5(完整目录)
文本/元素入场级联 cut-the-curve §6(waterfall entry)
场景内群组重定位(无剪辑) cut-the-curve §7(nudge curve)
光标主导动作 / 场景启动 / 变形点火 oversized-cursor
接缝渲染机制 / 白闪防护 seam-craft
产品发布 / 解说 / 字幕类工作 在对应上游技能之上叠加 text-beat-economicsbrand-faithfulcaptions-overlay 等 overlay 技能

实现层的入口是 cut-the-curve/SKILL.md:五类速度匹配的接缝技术(Zoom-Through、Inverse Zoom-Through、Cut the Curve、Waterfall Cut、Rack-Focus Blur-Cut)加上两个场景内技术(Waterfall Entry、Nudge Curve)。其统一原则是"在峰值速度处剪辑,剪辑两侧的方向与速度必须匹配"——doctrine 定法律,cut-the-curve 给参数。

创作顺序(Authoring Order)

Doctrine 规定了一条严格的流水线顺序,这也是本文后续各节的组织线索:

向量台账(ledger.json)→ STAMP 主时间轴接缝(seam-stamp.mjs --ledger ledger.json --write index.html)→ 每个阶段指派 sustained-motion 路线 → carriers 与 causes → 构建场景 → VERIFY(seam-gate.mjs)。

其中一条关键工程约定:手写只留给 Tier-A 的 morph/match-cut;凡是 stamp 出来的接缝"按构造即通过门禁"(stamped seams pass the gate by construction)——因为生成器写出的缓动、时长与符号本身就满足向量法则。

二、The Seam Law:接缝法则

2.1 Vector Law——"如何退出,决定如何进入"

How Scene A exits determines how Scene B enters: same axis, same direction, matched speed, cut mid-motion on both sides.

四条子规则:

  1. Axis(轴)——x 永远是 x,y 永远是 y,Z 永远是 Z。跨剪辑绝不允许换轴。
  2. Direction(方向)——绝不允许镜像。对 Z 轴,方向 = scale 变化的符号:变大 = push(镜头向前),变小 = pull(镜头向后)。"退场在后退(receding),入场却从小变大(grow-from-small)"是一个被镜像的向量——这是最常见的违规,原因正在于 grow-from-small 是元素入场的第一默认选择。
  3. Speed(速度)——入场初速度 ≈ 退场末速度,通过镜像缓动实现(退出 power4.in + 进入 power4.out,同距离同时长;进入侧从"名义路径"的 50% 处接上)。具体机制见 cut-the-curve
  4. Phase(相位)——剪辑必须落在两侧都在运动途中的位置。在剪辑前减速停住,或在剪辑后从静止开始,都是一个"死拍(dead beat)"。

2.2 The Current——一部片子只有一条"电流"

每部影片选定一条主导方向(仓库默认惯例:向左 LEFT),所有普通接缝都使用它。其他向量是预留(RESERVED)资源——花掉一个就意味着它在表达什么:

向量 含义
The current(向左) "下一拍"——中性的向前推进
向上(Upward) 升格——一个结论或揭示"高于"之前的一切
Z 向前(zoom-through) 钻入同一个思想的更深处
Z 向后(inverse zoom) 到达(ARRIVAL)——更大的东西落位
Scale-burst(炸出画面) 离开一个世界——表面从镜头前掠过

两条配套纪律:

  • 绝不允许相邻接缝方向相反——ping-pong(往返摆)读起来就是错误;
  • 方向变化必须有可见原因(点击 / 回弹 / 撞击)或章节边界。

2.3 Vector Ledger——先写账本,再动代码

在编写任何主时间轴之前,先把向量台账写成项目根目录下的 ledger.json(schema 见 references/seam-gate.md)。每个接缝一行:剪辑时间、exit 与 entry 的向量(轴 + 带符号方向,Z 行携带 scale 符号)、选择器、技术。Exit 与 entry 必须一致;某一行不匹配时,要修的是计划(plan),不是缓动。验证器在运行任何运行时采样之前,会先静态检查行的自洽性。

完整的 ledger.json 示例(三种接缝类型各一行):

{
  "fps": 30,
  "seams": [
    {
      "id": "hook→claim",
      "cut": 4.2,
      "technique": "cut-the-curve LEFT",
      "exit": { "selector": "#el-hook", "axis": "x", "dir": -1 },
      "entry": { "selector": "#el-claim", "axis": "x", "dir": -1 }
    },
    {
      "id": "claim→payoff",
      "cut": 10.2,
      "technique": "inverse zoom-through",
      "exit": { "selector": "#el-claim", "axis": "z", "dir": -1 },
      "entry": { "selector": "#el-payoff", "axis": "z", "dir": -1, "scanRoot": "#el-payoff" }
    },
    {
      "id": "ui→player (match cut)",
      "cut": 60.6,
      "type": "match-cut",
      "carrier": { "out": "#resting-card", "in": "#product-video" }
    }
  ]
}

各字段含义(来自 seam-gate.md 的 schema 说明):

  • cut——主时钟上的秒数,即"进入侧点火"的那一帧;
  • type——"cut"(默认,完整向量检查)/ "match-cut" / "morph"(只查载体连续性 + 零重叠;运动可以从边界才开始);
  • axis——"x""y""z"(z = scale)。dir——运动符号:x −1 = 向左,y −1 = 向上,z +1 = push(变大),z −1 = pull(变小);
  • selector——承载接缝运动的元素。主时间轴移动 wrapper 时用 wrapper;接缝运动写在子场景内部时用场景内的 hero(id 或 [data-hf-id=…])。用 probe 命令能帮你判断该用哪个;
  • entry.scanRoot(z 接缝)——扫描"符号对抗"内部入场的子树,默认取 entry 选择器;
  • carrier——"cut" 行可选,match-cut/morph 必填。out/in 的矩形必须在 cut±1 帧匹配(中心 12px / 尺寸 5% 容差,含祖先变换)。

2.4 Carriers——眼睛跟随的是物体,不是抽象概念

最强的接缝会把一个具体载体以匹配的位置和速度递过剪辑:走到一半的光标、收缩/停靠进下一个布局的容器、飞入精确槽位的标记、waterfall 剪辑中的词组。若没有天然载体,就让场景的 hero 元素来承担(部分位移 + 提前淡出,入场时在飞行途中)。绝不允许 crossfade——它根本没有任何载体。

2.5 Causal Motion——因果运动链

让每个动作都 visibly 由上一个动作点燃:click → squash → release spring → flight → impact → recoil → reveal。三条规则:

  • 效果在原因帧上启动——同一时间轴位置,绝不"稍后一点";
  • 反应幅度随暗示质量缩放:大元素回弹慢,小元素啪地一下;
  • 力是改变方向的许可证;无因的方向翻转就是 ping-pong。

三、The Seam Gate:把法则变成构建门禁

Doctrine 给 Seam Gate 的定义是:"build gate——运行验证器,exit 0 才算接缝完成":

node <SKILL_DIR>/scripts/seam-stamp.mjs --ledger ledger.json --write index.html  # 生成
node <SKILL_DIR>/scripts/seam-gate.mjs  verify --ledger ledger.json --project .  # 验证

在仓库中,<SKILL_DIR>.agents/skills/motion-doctrine 所在目录。两个脚本零 npm 依赖,要求 Node ≥ 22 加一个本地 Chrome(gate 会自动找 ~/.cache/puppeteer 下的 chrome-headless-shell 或系统 Chrome)。

3.1 seam-stamp.mjs:从台账"盖章"出接缝代码

seam-stamp.mjs 是 Seam Gate 的"生成半边":读取 ledger.json,输出(或 --write 写回)主时间轴的接缝代码块。从源码可以看到它的生成逻辑:

  1. 场景清单与基础态:按出现顺序收集所有 exit/entry 选择器,第一个场景 gsap.set 为可见(autoAlpha: 1),其余全部 autoAlpha: 0 归零变换;Z 型入场会被预置到"离焦状态"——pull(dir −1)预置 scale: 1.25,push 预置 scale: 0.78,并带上 blur(18px)(见 seam-stamp.mjs#L37-L45)。
  2. X/Y 接缝seam-stamp.mjs#L108-L120):
    • exit:tl.to(sel, { xPercent: 12*dir, autoAlpha: 0, duration: 0.34(默认), ease: "power3.in" }),起始于 cut - 0.34s
    • 剪辑点:tl.set(sel, { autoAlpha: 0 }, cut) 硬切;
    • entry:tl.fromTo(sel, { xPercent: -travel*dir, autoAlpha: 0.35 }, { xPercent: 0, autoAlpha: 1, duration: 0.42(默认), ease: "power4.out", immediateRender: false }, cut)——从路径中途(0.35 不透明度)点火,正好落实 Vector Law 的"≥50% 接上"与 Phase 规则。
    • 台账可选参数:exit.dur / entry.dur 覆盖时长,entry.travel(xPercent/yPercent 偏移,默认 10,柔化入场用 8),blur(Z 接缝,默认全画面 18px,文字 scale 用 10px)。
  3. Z 接缝seam-stamp.mjs#L92-L107):pull 时 exit scale: 1 → 0.8 + blur,entry 1.25 → 1expo.out;push 时 exit 1 → 1.18,entry 0.78 → 1。退出时长默认 0.21s、进入默认 0.5s——进入约为退出的 2.4 倍,与 cut-the-curve 中 cut-the-curve 类接缝"entry ≈ exit × 1.27"、inverse-zoom "30% exit / 70% entry"的参数族一致。退出透明度是独立的 none 缓动 tween(避免 power3.in 把不透明度拖得太久)。
  4. 防呆与标记块:若同一行的 exit/entry 轴或方向不一致,脚本直接抛错 "fix the PLAN, not the stamp"(seam-stamp.mjs#L87-L90)。写回 HTML 时替换 // <seams:auto>// </seams:auto> 之间的块;无标记则插入到 window.__timelines["main"] = tl; 注册行之后(seam-stamp.mjs#L130-L140)。match-cut/morph 行只生成两侧的 tl.set 可见性开关,载体交接保持 Tier-A 手写。

一个值得注意的源码细节:生成器给 X/Y 的退出腿用的是 power3.in(而非 doctrine 文字里写的 power4.in),而验证器的 speed-match 检查对速度比的容忍度为 3 倍(下文 SPEED_RATIO),因此该组合在数值门禁上完全通过——这说明"镜像缓动"在数值层面被落实为可测的速度比,而不是对缓动函数名的字面匹配。

3.2 seam-gate.mjs:无头浏览器里的数值验收

seam-gate.mjs 是"验证半边",零 npm 依赖,通过**原始 CDP(Chrome DevTools Protocol)**驱动 chrome-headless-shell。它的工作方式:

  • --project <dir>新起一个预览服务器npx --yes hyperframes preview --foreground --no-open --port <5380+随机20>,并刻意删除 HYPERFRAME_RUNTIME_URL 环境变量——源码注释说明错误值会静默地以 200 HTML 通过探测),等待 /api/projects 就绪后打开 …/preview/comp/index.html,跑完杀掉子进程;--url 则复用已运行的服务器(注意:comp 编辑后要重启它,否则验的是过期 bundle);
  • 等待 HyperFrames 运行时(window.__playerReady && window.__renderReady && window.__player)与 document.fonts.ready 后注入页内 harness:seek(t)__player.pause() + __player.seek(t) 逐帧定位,read()getBoundingClientRect 读中心坐标、宽比(有效 scale,自动包含祖先 wrapper 的变换)与祖先链累计不透明度
  • 对每个接缝在 cut-0.1scut-1fcut+1fcut+0.1s 四个时刻采样,再做以下检查。

从源码顶部常量区(seam-gate.mjs#L37-L44)可以看到门禁的全部数值判据

常量 含义
VIS 0.04 累计不透明度低于此值 = 不可见
EPS_XY 15 px/s 慢于此视为"静止"(判 exit-settled / entry-from-rest)
EPS_Z 0.04 es/s Z 轴静止阈值(有效 scale 单位/秒)
SPEED_RATIO 3 入/退速度比超出 3 倍或小于 1/3 → WARN
CARRIER_POS_TOL 12 px 载体中心跨剪辑容差
CARRIER_SIZE_TOL 5% 载体尺寸跨剪辑容差(含祖先 scale)

每个检查与报告行的对应关系(与 seam-gate.md 的"What each check enforces"表一致):

报告行 对应法则
ledger 计划内 exit/entry 向量自洽(axis + dir)
exit-moving / entry-moving 规则 1/4——不许停住的退出、不许静止起步的进入
exit-direction / entry-direction 规则 2——实测运动符号必须等于台账符号(Z 轴即 scale 符号规则)
speed-match(WARN) Vector Law §3——入场速度 ≈ 退场速度
zero-overlap 逐帧只允许一侧可见——剪辑不是溶解
z-sign-scan 规则 2 的扩展——进入场景自身的内部入场(在 cut+1f → cut+0.1s 窗口内扫描 scanRoot 子树)不得与接缝 Z 符号对抗
carrier-position / carrier-size / carrier 载体矩形连续性,含祖先变换

速度与可见性的测量基于 getBoundingClientRect(x/y 用中心,z 用宽度比),因此祖先 wrapper 的变换自动被计入——这正是"台账里 selector 该写 wrapper 还是子场景 hero"要靠 probe 来定夺的原因。

此外还有 probe 子命令:

node <SKILL_DIR>/scripts/seam-gate.mjs probe --t 44.8 --project <project-dir>

它在指定剪辑时刻前后(默认 ±0.1s 窗口)对 #root 全树扫描(上限 900 个元素),按速度排序输出 top 14 个 mover 的选择器、vxvyvscale、不透明度变化与尺寸,并提示符号约定(x-: 左, y-: 上, scale+: push, scale-: pull)——即"为写台账行做侦察"。verify 支持 --json 机器输出、--fps(默认 30),退出码 0 = 通过、1 = 存在 FAIL、2 = 用法/环境错误。

3.3 脚本查不了、仍然由你负责的三条规则

  1. 编辑会重新打开接缝:任何对场景头/尾约 1 秒的改动(包括为匹配新 VO 重排时间)都会使该边界失审——重跑验证器。
  2. 音频是时钟(Audio is the clock):按 VO 的真实词时间戳重排场景,绝不为塞进时间槽而赶读;VO 重新生成会重开它的接缝。
  3. clip-gating 陷阱(zero-overlap FAIL 的常见原因):data-start 早于入场 tween 的 clip,会以初始不透明度被揭示——必须同时设 autoAlpha: 0 并且data-start 等于剪辑时间,绝不能更早。

这套门禁在仓库内的实战出口是周度 changelog 视频流程:changelog-video 技能将每周 changelog 渲染为 1080×1080 MP4,验收标准为 hyperframes check(0 errors)+ seam-gate verify(0 fail/warn),见 .agents/skills/README.md

四、Part 2 — Performance:场景要一直在"表演"

4.1 禁止 idle wobble:运动必须"表演",不许"呼吸"

闲置正弦循环(breathe、float、drift、glow pulse)被禁止作为持续运动——它们读起来就是"视频在等待"。一个场景入场完毕却还剩好几秒,是规划 bug:该加故事,不该加晃动。入场与退场之间的每一段都必须由以下路线之一独占(在计划里指名路线):

路线 内容
Staged reveals 把内容扣住,在解说拍点上兑现——画面持续获得新信息(≥2 个内容组时的默认)
Camera with intent 一条映射好的 scale+pan 路径:广角建立 → 行进 → 落在主体上
Sequenced UI life 产品随时间"活起来":进度前进、高亮步进、计数滚动
Animated sequences 元素演出一拍:卡片归栈、条目被拖走、结果组装
Cursor-led action 超大号光标带着眼睛走到触发器;它的 CLICK 点燃下一拍(oversized-cursor

验收测试:在任意一秒暂停——必须有一个"有意义的东西"正在飞行途中(一个 reveal 正在落位、镜头正在行进、UI 正在做解说所说的事)。

4.2 Stillness before climax——高潮前的静默

在重大动作与其结果之间安排 0.3–0.75s 的停顿——戏剧逗号。直接从动作跳到结果的场景会丢掉它。

4.3 Timing intents

  • 单个入场 ≤ ~800ms;更长的铺垫应做成多元素 stagger,而不是一个慢元素;
  • 退出 ≈ 入场的 75%。例外:cut-the-curve 把这条比例反转(入场约为退出的 127%)——这正是"接住速度"的代价;
  • 总 stagger ≤ 500ms;8 个以上元素时收紧逐项延迟,或只 stagger 前几个;
  • 禁用缓动bounce.out / elastic.out。入场过冲 back.out(1.4–1.7) 可以;
  • 相似元素共享同一 ease+duration 意图——绝不为每个元素发明独特的配对。

4.4 Transition vocabulary——全片只用 2–3 种场景间过渡

并反复使用;默认边界就是沿 current 方向的 cut-the-curve。手写的共享元素 morph(intent: morph)不计入该配额。

五、Anti-Patterns:一张对照表记住所有禁区

Doctrine 收尾的这张表浓缩了全文判罚逻辑:

不要 而要
孤立地设计每个场景的入场 先写向量台账
场景间 crossfade 沿 current 方向的 cut-the-curve
退场完成后切场景 两侧都在运动途中剪辑
剪辑后从静止开始入场 从名义路径 ≥50% 处进入
inverse-zoom 退场 → grow-from-small 入场(或 push → 过度收缩) 匹配 scale 速度符号(Seam Gate z-sign-scan
Z 接缝交接下,来场自身弹跳式 intro 让开场帧保持已构图状态,或让符号匹配
用 idle wobble / breathe / float 填时间 指派一条 sustained-motion 路线;或加故事
无因的方向翻转 花掉一个"力",或保持 current
把预留向量当花哨用法 默认 current;把它们花在意义
反应比原因晚几帧 同帧点火
动作直接跳到结果 安排 stillness-before-climax(0.3–0.75s)

六、技能生态中的位置与适用前提

  • 两套技能命名空间.agents/skills/ 是"仓库原生"技能(只在本仓库运行时被 Codex CLI 自动发现,承载 doctrine 重的创作流,如周度 changelog 视频);skills/ 则是通过 npx hyperframes skills 分发给其他项目的市场技能集。.claude/skills/ 下有一份字节级镜像(CI 有 check-skill-mirror.mjs 强制同步),供 Claude Code 用户获得同样的自动发现。
  • 依赖闭包changelog-video 的五个依赖技能(motion-doctrinecut-the-curvecaptions-overlayseam-craftoversized-cursor)就地位于 .agents/skills/ 下,使路由图在 clone 后即完整。
  • 运行前提:Node ≥ 22、可用的无头 Chrome(CHROME_PATH~/.cache/puppeteer 下自动发现)、一个能通过 hyperframes preview 起预览服务的项目目录;--project 模式会在 5380–5399 端口段随机选口、用完即杀,不需要你手动管理服务器生命周期。

适用边界:motion-doctrine 是针对"多场景、有主时间轴、可 seek 的 HyperFrames 影片"的接缝与节奏纪律;它对单场景动画不构成额外约束(此时直接读 cut-the-curve 的 §6/§7 场景内技术即可)。它的价值不在"更花哨",而在于把"多场景像一次连续镜头运动"从一个审美直觉变成一份可静态检查(ledger 自洽)、可数值验证(verify 退出码 0)、可再生(stamp 幂等替换 <seams:auto> 块)的工程契约。

相关文件索引

登录后查看全文
热门项目推荐
相关项目推荐