OpenMontage 中的 Remotion 过渡动画移植指南:把 @remotion/transitions 翻译为 HyperFrames 交叉淡化与 shader-transitions
【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage
本篇技术指南聚焦于 Remotion 到 HyperFrames(HF)迁移链路中最容易翻车的一环——场景转场(scene-to-scene transitions)。OpenMontage 仓库中内置了remotion-to-hyperframes技能(详见 .agents/skills/remotion-to-hyperframes/SKILL.md),它以 Remotion 源码为输入,产出等价的 HF HTML + GSAP 组合。阅读完本文,你将掌握:如何把 Remotion 的TransitionSeries/@remotion/transitions预置转场翻译成 HF 的"场景重叠 + GSAP 时间轴"模型、八种presentation的逐项对照方案、帧到秒的时序换算规则,以及何时应该放弃手工翻译、改用运行时互操作(runtime interop)模式。
为什么要专门翻译转场
@remotion/transitions是 Remotion 官方提供的场景间转场库,而 HyperFrames 是一个"从 HTML 渲染视频"的框架:组合就是一个 HTML 文件,DOM 通过data-*属性声明时序,动画由可寻址(seekable)的 GSAP 时间轴驱动。两者的时间模型根本不同——Remotion 以帧为单位、通过 React 组件树驱动,HF 以秒为单位、通过 DOM + GSAP 时间轴驱动。因此转场不能逐行照搬,必须做两层翻译:
- 结构翻译:
<TransitionSeries>这种"序列 + 重叠窗口"的组件结构,要翻译成"场景 div 在时间轴上重叠"的扁平 DOM 结构; - 动效翻译:Remotion 的
presentation(淡化、滑动、擦拭、翻页……)要翻译成 HF 的 GSAP tween 或 shader-transition。
根据 .agents/skills/remotion-to-hyperframes/SKILL.md 的说明,HF 提供两条翻译路径:
| 路径 | 适用场景 | 成本 |
|---|---|---|
| 手工 GSAP 交叉淡化 | 简单的透明度 / 变换类转场 | 免费,无需额外依赖 |
| HF shader-transitions 包 | 视觉丰富、与@remotion/transitions预设相当的转场(iris、ripple、zoom、glitch 等) | 通过npx hyperframes add <block>安装 |
核心模式:<TransitionSeries>就是带重叠的<Series>
Remotion 中一个典型的转场组合长这样:
<TransitionSeries> <TransitionSeries.Sequence durationInFrames={60}> <SceneA /> </TransitionSeries.Sequence> <TransitionSeries.Transition presentation={fade()} timing={linearTiming({ durationInFrames: 15 })} /> <TransitionSeries.Sequence durationInFrames={60}> <SceneB /> </TransitionSeries.Sequence> </TransitionSeries>翻译成 HF 时,核心思路是:让两个场景在时间轴上重叠一个"转场时长"的窗口。以 fps=30 为例(60 帧 = 2 秒,15 帧 = 0.5 秒):
- SceneA:
[0, 60]→data-start="0">// Manual fade (presentation={fade()}) tl.to(sceneA, { opacity: 0, duration: 0.5, ease: "none" }, 1.5); tl.fromTo(sceneB, { opacity: 0 }, { opacity: 1, duration: 0.5, ease: "none" }, 1.5);注意
ease: "none"——它精确对应 Remotionfade()默认的线性插值,是转场与画面时序对齐的关键。durationInFrames: 15在 30fps 下换算为 0.5 秒,这就是 tween 的时长;两个 tween 都锚定在 1.5 秒(即 SceneB 的data-start),从而在重叠窗口内完成"旧场景淡出 + 新场景淡入"的交接。这里的
data-start/data-duration是 HF 组合的通用契约:根元素上还有data-composition-id、data-width/data-height、data-fps等必需或可选属性,完整属性表见 .agents/skills/hyperframes-core/references/data-attributes.md。多场景转场的硬性规则
在 HF 的转场体系里,.agents/skills/hyperframes-animation/transitions/overview.md 明确了几条不可妥协的规则,翻译时同样适用:
- 转场就是退场:禁止在转场触发前用
gsap.to()把旧场景的元素逐个移出(那是"带凹陷的硬切",不是转场);旧场景内容在转场开始时必须完全可见,交接由转场本身完成。 - 入场景必须用
gsap.fromTo()(而非gsap.from()):from()是向当前 CSS 状态动画,如果配对 CSSopacity: 0会形成 0→0 的无效动画,元素永远不会出现。 - 旧场景与新场景必须在同一时刻 T 开始动画:运动本身就是交接。例如
T = 4.0时,tl.to("#s1", { yPercent: -100, ... }, T)与tl.fromTo("#s2", { yPercent: 100 }, { yPercent: 0, ... }, T)成对出现。 - 退场动画仅允许出现在最后一个场景(如结尾淡出黑场)。
Presentation 对照表:八种 Remotion 预设的 HF 翻译
transitions.md中给出了@remotion/transitions全部presentation预设的完整对照方案,这是整个翻译工作的核心索引表:Remotion presentationHF 翻译方案 fade()手工 gsap.to(opacity)交叉淡化slide({direction: "from-right"})入场 gsap.fromTo(translateX: "100%" → 0)+ 出场to(translateX: "-100%")wipe({direction: "from-left"})入场 gsap.fromTo(clip-path: inset(0 100% 0 0) → inset(0 0 0 0))clockWipe()使用 HF 的 sdf-irisshader-transition(npx hyperframes add sdf-iris)flip()gsap.to(rotateY)180° 在两个场景之间分摊cube()使用 HF 的 cinematic-zoom,或用rotateY+transform-origin手工构建iris()使用 HF 的 sdf-irisshader-transitionnone()无转场,在边界处硬切 逐条解读几个重点:
slide()是典型的双端动画:新场景从屏幕外(translateX: 100%)滑入归位,同时旧场景向反方向(-100%)滑出,两者在同一时间轴位置并行,制造"推入"的连续感。wipe()走 clip-path 路线:只动画新场景的裁剪区域,inset(0 100% 0 0)(全遮)过渡到inset(0 0 0 0)(全开),旧场景不动,形成"擦拭揭示"的效果。这正对应 HF 转场目录中的 CSS 径向/遮罩类转场。flip()/cube()属于 3D 类:需要rotateY旋转与transform-origin配合;cube()也可直接用 HF 的cinematic-zoomshader 替代,获得更丰富的透视效果。clockWipe()/iris()强烈建议走 shader 路线:这类带形状遮罩的转场用 CSS 手工模拟成本高、观感差,而 HF 的sdf-irisshader-transition 是现成的 GLSL 实现(见下文)。
值得注意的是,HF 的 CSS 转场目录本身就覆盖了这些类别:推入/滑动(css-push.md)、径向/形状(css-radial.md)、3D 翻转(css-3d.md)、缩放(css-scale.md)、溶解(css-dissolve.md)等,目录索引见 .agents/skills/hyperframes-animation/transitions/catalog.md。翻译时如果目标预设恰好有同族的 HF 现成实现,直接参考对应
css-*.md的 GSAP 代码,比从零手写更稳。转场的情绪与节奏选择
overview.md 还给出了转场选择的语义化指导,翻译时可以用来判断"这个 Remotion 转场在 HF 里该不该换一种表达":转场向观众传达两个场景的关系——交叉淡化表示"内容在延续",推入滑动表示"进入下一个要点"。同时提供能量等级映射(平静 0.5–0.8s / 中等 0.3–0.5s / 高 0.15–0.3s)、情绪到转场类型的映射表(如 Tech 风格配 grid dissolve / glitch,Cinematic 配 zoom through / gravity drop),以及叙事位置的节奏建议(开场用最具辨识度的转场 0.4–0.6s,转场高潮用最猛的 accent)。翻译 Remotion 预设时,若目标片段的情绪与预设表达不符,应当换用更合适的 HF 转场族,而不是机械照搬。
时序换算:帧、秒与 easing 的对应关系
Remotion 以帧为时间单位,HF 的 GSAP 时间轴以秒为单位。换算必须在翻译阶段一次性完成,而不是留到运行时:
time_seconds = frame / fps以 fps=30 为例:15 帧 → 0.5s,30 帧 → 1.0s,90 帧 → 3.0s。
@remotion/transitions的timing参数翻译规则如下:linearTiming({durationInFrames: 15}) → ease: "none" linearTiming({durationInFrames: 15, easing: ...}) → ease 按 easing 对照表(见 timing.md) springTiming({config: {damping: 12}}) → ease: "back.out(1.4)"(约 0.7 秒)linearTiming的默认行为是线性插值,对应 GSAP 的ease: "none";- 带自定义
easing时,按 .agents/skills/remotion-to-hyperframes/references/timing.md 中的Easing→ GSAP 映射表换算,例如Easing.out(Easing.cubic)→power3.out、Easing.inOut(Easing.cubic)→power3.inOut、Easing.bezier(a,b,c,d)→CustomEase(需 CustomEase 插件); springTiming是整个翻译中损耗最高的部分,{damping: 12, stiffness: 100, mass: 1}(干脆型)近似为back.out(1.4)、约 0.7s,这一映射在 T2/T3 层级上经过 SSIM 实测验证;粗略经验公式为back.out(N)的过冲比 ≈(stiffness / damping²) × 1.4。
同样地,转场时长也要除以 fps 换算成秒:
linearTiming({ durationInFrames: 15 })在 30fps 下就是 0.5s 的 tween 时长。何时使用 HF shader-transitions
对于 Remotion 预设中存在视觉丰富的 GLSL 等价物的转场(iris、ripple、zoom、glitch 等),transitions.md 明确建议改用 HF 的 shader-transitions 包——它们比手工 GSAP 变换产生的画面更丰富(逐像素 warp、溶解、形变是 CSS 做不到的)。先安装对应 block:
npx hyperframes add sdf-iris然后在组合中声明 shader 转场元素:
<div id="iris-transition" class="hf-shader-transition">var tl = HyperShader.init({ bgColor: "#000", accentColor: "#6366f1", scenes: ["s1", "s2", "s3", "s4"], transitions: [ { time: 4.0, shader: "sdf-iris", duration: 0.7 }, // WebGL shader { time: 8.5, duration: 0.8 }, // 无 shader → CSS 交叉淡化 { time: 13.0, shader: "domain-warp", duration: 0.6 }, // WebGL shader ], });使用 shader 转场时的 CSS 约束
由于 shader 转场通过 html2canvas 把 DOM 场景捕获为 WebGL 纹理,而 canvas 2D 渲染管线与 CSS 并不完全一致,overview.md 列出了 6 条"shader 兼容 CSS 规则",翻译产出物必须遵守,否则转场边界会出现可见伪影:
- 渐变中禁用
transparent关键字(canvas 会插值成rgba(0,0,0,0),产生黑色边缘),必须写目标色零透明度形式如rgba(200,117,51,0); - 厚度小于 4px 的元素禁用渐变背景(canvas 无法匹配 1–2px 元素的渐变渲染),细线条用纯
background-color; - 捕获期间可见的元素禁用 CSS 变量
var()(html2canvas 不能可靠解析自定义属性),内联样式用字面颜色值; - 无法满足上述规则、又不参与捕获的装饰元素标记
data-no-capture; - 渐变透明度不要低于 0.15;
- 每个
.scene必须有显式background-color,且与init()配置中的bgColor一致(两者缺一,纹理渲染为黑色)。
这些约束只作用于 shader 转场组合;纯 CSS 组合无此限制。另外,catalog.md 还给出了一张"不要用"清单:star iris(多边形插值损坏)、tilt-shift(CSS 无选择性模糊)、lens flare(可见形状而非光学效果)、hinge/door(变形过快),翻译时如果 Remotion 源用到同类效果,应替换为 HF 支持的同语义转场。
当源使用自定义 Presentation
Remotion 支持自定义
presentation实现:const customPresentation: PresentationComponent = ({ children, presentationProgress, presentationDirection, }) => { return ( <div style={ { /* compute transform from progress */ } } > {children} </div> ); };翻译方法分两种情况:
- 常规情况:把
style={...}里的数学提取出来,生成等价的 GSAP tween。presentationProgress(0→1 的归一化进度)驱动的变换公式,直接映射为一个以progress为参数的gsap.to(target, { transform: ... })。 - 逃逸情况:如果自定义 presentation 内部用
useCurrentFrame()驱动了超出简单进度曲线的动画(比如依赖具体帧号的非确定性逻辑),则该源应判定为不可翻译,转交运行时互操作模式——见 .agents/skills/remotion-to-hyperframes/references/escape-hatch.md。
逃逸到运行时互操作模式
escape-hatch.md定义了完整的"何时放弃翻译"规则:先用 .agents/skills/remotion-to-hyperframes/scripts/lint_source.py 对 Remotion 源做静态检查,一旦命中任何 blocker(useState/useReducer驱动动画、非空依赖的useEffect/useLayoutEffect、异步calculateMetadata、第三方 React UI 库如 MUI/Chakra/Mantine/antd/shadcn/Radix/NextUI),就停止翻译并推荐互操作模式。因为这些模式破坏 HF 依赖的"可寻址、确定性帧"模型,硬翻会产出"看起来对、实际错"的结果。互操作模式的本质是:用 esbuild 把 Remotion 代码与 React +
@remotion/player打包,在 HF 组合的 HTML 里挂载一个暂停的<Player>,注册到window.__hfRemotion(暴露seekTo(frame)、pause()、durationInFrames、fps),由 HF 的渲染循环逐帧seekTo(frame)驱动。这样useState、useEffect、MUI 组件全部照常工作——因为渲染交给了 Remotion 自己的 React reconciler。需要注意:一个 blocker 就足以判定整段不可翻译,即使其余部分很干净;用户要么整体走互操作,要么先重构掉 blocker 模式。与 blocker 相对的是 warning 类(
@remotion/lambda配置、delayRender、useCallback/useMemo、纯自定义 hook、staticFile、interpolateColors),这些不阻断翻译——按 escape-hatch.md 的规则丢弃包装或内联函数后照常翻译,并在TRANSLATION_NOTES.md里记录缺口。端到端工作流与质量验证
翻译转场不是"写完就算",SKILL.md 定义的五步工作流强调用 SSIM 实测把关:
- Lint 源:跑
lint_source.py,识别 blocker(停止 + 推荐互操作)与 warning(可翻译,丢弃构造并记录); - 规划:按源使用的 API 查 api-map.md 定位所需参考文档,用到
TransitionSeries/@remotion/transitions就加载 transitions.md(即本文主题); - 生成:产出
index.html——根<div id="stage">携带data-composition-id/data-start/data-duration/data-fps/data-width/data-height与每个标量 prop 对应的data-*;场景 div 平铺并声明data-start/data-duration/data-track-index;CSS 设置所有动画属性的from态;底部单个<script>内建一条gsap.timeline({ paused: true }),并把window.__timelines["<composition-id>"] = tl注册给 HF 运行时; - 验证:按 eval.md 渲染两边基线并做 SSIM 对比(阈值约为源复杂度层级 p05 之下 0.02);失败时用 frame_strip.sh 定位具体分歧帧。关键前提:两个渲染必须使用一致的像素格式——在 Remotion 源的
remotion.config.ts中设置Config.setVideoImageFormat("png")+Config.setColorSpace("bt709"),否则对比测到的是编码器差异(约 0.05 SSIM 损失)而非翻译保真度; - 记录缺口:无法干净翻译的内容(被丢弃的音量坡道、被近似的自定义 presentation、被替换的字体)写入
TRANSLATION_NOTES.md。
技能自带的四级测试语料(assets/test-corpus/run.sh)把这一验证固化为回归测试:T1 单元素淡入(SSIM 0.974,阈值 0.95)、T2 多场景 + spring + 音频 + 图片(SSIM 0.985,阈值 0.95)、T3 数据驱动 + 自定义子组件 + 数字滚动(SSIM 0.953,阈值 0.90)、T4 逃逸场景 lint 校验(8/8 通过)。其中转场与 timing 相关的映射(
ease: "none"线性插值、spring →back.out、count-up →power3.out)正是在 T1–T3 上被逐一实测验证的——这也是本指南所有换算规则的可信度来源。小结
把
@remotion/transitions移植到 HyperFrames 的关键可以浓缩为三句话:- 结构上,
<TransitionSeries>等于"场景按转场时长重叠",重叠窗口用data-start/data-duration在秒级时间轴上声明; - 动效上,简单转场走 GSAP(
fade/slide/wipe/flip等),视觉丰富的转场(clockWipe/iris/ 自定义 GLSL)走@hyperframes/shader-transitions,且两者可在同一组合中混用; - 边界上,所有帧 → 秒换算在翻译期完成,所有 easing 按 timing.md 对照表换算,凡是依赖
useCurrentFrame()做进度曲线之外动画的自定义 presentation,一律交还 运行时互操作模式。
最后用 SSIM 渲染对比兜底,保证"看起来对"的转场在像素层面也真的对。
【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.
项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage
- 转场就是退场:禁止在转场触发前用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考