Remotion 视觉制作技能
基于创建 SureCart 4.2 发布视频的实际生产经验构建。 作者:Chandresh Vaghanani
开始之前——2 个问题(不可协商)
在编写任何代码之前,请先问这 2 个问题。保持随意——一条消息,两个问题。即使用户说“直接开始”或“现在就开始”,也不要跳过它们。这 10 秒钟可以避免数小时的返工。
开始之前——两件小事:
1. 浅色还是深色背景?(或者分享你的网站/截图,我会从那里获取)
2. 你有产品截图或 Mockup 吗?(完全可选——没有我也可以构建)
就这样。除非用户主动提供更多信息,否则不要再问其他问题。
如何处理答案:
- 如果他们分享了 Mockup/截图——提取背景色、强调色、字体样式、UI 文案。使用它们。
- 如果他们分享了网站 URL——获取它,提取相同信息。不要猜测。
- 如果他们只回答了浅色/深色——使用下面的无信息回退默认值继续。
- 如果他们跳过了这两个问题——使用下面的无信息回退默认值继续。不要再问。
其他资产(Logo、音乐、额外颜色)——如果用户提供了就接受,但永远不要因此阻塞。
无信息回退默认值
当品牌信息不可用时,AI 的本能是缩小字体并增加内边距。这种本能在视频中是完全错误的。请应用以下设置:
当没有提供 Mockup、截图或网站时,这些设置会自动启用。即使没有任何品牌信息,也能确保专业的结果。
布局
- 每个场景的 AbsoluteFill 上都使用
justifyContent: "center"+alignItems: "center"—— 没有例外 - 使用
marginTop: -50将内容稍微拉至真正中心的上方 - Mockup/卡片宽度:1200px(使用上限——填满画面)
- 容器内边距:最多 40px —— 绝对不能更多
- 内容必须至少占据 1080px 高度的 75%。如果未达到,请增大字号或减少间距。
字号(回退——使用范围的上限)
| 元素 | 回退大小 |
|---|---|
| 主标题 | 140px |
| 副标题 | 56px |
| 正文/描述 | 36px |
| 标签/药丸 | 22px |
| 卡片内容文本 | 28px |
颜色(回退调色板——安全且专业)
- 背景:
#0a0a0f(接近黑色,中性) - 强调色:
#6366f1(靛蓝色——可读、现代、不特定于任何产品) - 文字:
#ffffff - 柔和文字:
rgba(255,255,255,0.55) - 卡片背景:
rgba(255,255,255,0.06)
这些仅仅是起点。一旦用户分享了品牌颜色,就完全替换它们。
在没有品牌信息时不要做什么
- 不要使用霓虹色/赛博色——那是个人审美,不是品牌匹配
- 不要使用
paddingTop: 120并称之为“居中”——那实际上是顶部对齐,下方有死区 - 不要因为“看起来干净”就为主标题选择
fontSize: 32——在视频上它将无法辨认
规则 #1——画布不是网站
这是 AI 生成的 Remotion 视频中最常见的错误。
Remotion 画布是 1920×1080px。设计时需填满它——像网页那样设计是行不通的。
⚠️ 这些是最小值。将正文文字写成
fontSize: 18是一个错误,而不是风格选择。 低于最小列的任何值都意味着文字在 1080p 屏幕上无法阅读。 将这些数字视为类型错误——在继续之前必须修复它们。
| 元素 | 最小值(硬下限) | 推荐范围 |
|---|---|---|
| 主标题 | 80px | 100–160px |
| 副标题 | 40px | 48–72px |
| 正文/描述 | 28px | 32–40px |
| 标签/药丸/徽章 | 18px | 20–24px |
| 卡片内容文本 | 22px | 24–32px |
规则:
- 内容必须感觉填满了屏幕
- Mockup 卡片:
width: 1100–1200px,居中 - 容器内边距:最大 48px——而不是像网页那样的 80–120px
- 如果感觉文本“大得不舒服”——可能对于视频来说刚刚好
规则 #2——所有动画必须使用 useCurrentFrame()
// 正确
const frame = useCurrentFrame();
const opacity = interpolate(frame, [0, 20], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
// 禁止——无法正确渲染
// CSS: transition: opacity 1s ease
// Tailwind: animate-fade-in
// CSS @keyframes
始终在每个 interpolate() 调用中包含 extrapolateLeft: "clamp" 和 extrapolateRight: "clamp",除非你明确需要无界值。忘记这样做会导致值在范围外溢出并产生微妙的错误。
规则 #3——确定性
// 禁止——每次渲染传递都会产生不同的值
const x = Math.random() * 100;
// 正确——每次、每帧的值都相同
import { random } from "remotion";
const x = random("particle-x-1") * 100; // 字符串种子 = 确定性
规则 #4——资产
import { staticFile } from "remotion";
import { Img } from "remotion"; // 而不是原生 <img>
import { Audio } from "@remotion/media";
// 对于 public/ 文件夹中的任何内容,始终使用 staticFile()
<Audio src={staticFile("bg-music.mp3")} />
<Img src={staticFile("logo.png")} /> // <Img> 确保资产在帧渲染前加载
// 远程 URL 不需要 staticFile(),但需要 CORS
规则 #5——流畅的预览播放
在 <Sequence> 组件上添加 premountFor={30},以便重型组件在其时间到达前预加载 30 帧:
<Sequence from={90} durationInFrames={120} premountFor={30}>
<HeavyScene />
</Sequence>
Easing 配方
import { Easing } from "remotion";
// 清晰的 UI 入场(强缓出,无过冲)——大多数入场动画
Easing.bezier(0.16, 1, 0.3, 1)
// 缓慢的编辑淡入淡出(均衡的缓入缓出)——情绪过渡
Easing.bezier(0.45, 0, 0.55, 1)
// 俏皮的过冲——有弹性/有趣元素
Easing.bezier(0.34, 1.56, 0.64, 1)
// 弹簧——无弹跳(Mockup 入场、卡片、UI)
spring({ frame, fps, config: { damping: 200 } })
// 弹簧——略有弹跳(冲击力强的元素)
spring({ frame, fps, config: { damping: 20, stiffness: 200 } })
// 弹簧——有弹性/俏皮
spring({ frame, fps, config: { damping: 8 } })
预布局门控——在编写任何场景布局之前运行
这些不是建议。在编写任何场景的第一个 <div> 之前,运行所有三个门控。如果任何门控失败,请更改布局——永远不要用固定尺寸或额外内容来修补。
门控 1——画布填充(两个轴,强制)
宽度轴:
dead_side = (1920 − content_width) / 2
必须 ≤ 300px。如果 > 300px → 内容太窄。加宽它或更改布局。
高度轴:
fill_h = content_height / 1080
必须 ≥ 0.80。如果 < 0.80 → 内容太短。更改布局形状。
⚠️ 如果高度失败,不要通过为卡片或容器设置固定高度来修复。 固定高度会在卡片内创建一个空洞——死区只是移到了元素内部,而不是被消除。唯一的真正修复是更改布局形状(门控 2)。
现实中的失败示例——3 个短功能卡片:
| 版本 | content_width | dead_side | content_height | fill_h | 判定 |
|---|---|---|---|---|---|
| v1: 3 列卡片 (365px) | 1151px | 384px | 416px | 38% | ❌ 两个轴均失败 |
v2: 强制 height: 720px | 1542px | 189px | 864px | 80% | ✅ 宽度,❌ 视觉(卡片内空洞) |
| v3: 水平行 (1400px) | 1400px | 260px | 916px | 85% | ✅ 两个轴均通过 |
门控 2——布局选择决策表
内容量决定了布局形状。在编写代码之前,从此表中选择布局。
| 项目数量 | 每项内容 | 正确布局 | 原因 |
|---|---|---|---|
| 3–5 项 | 图标 + 名称 + 1 行 | 水平行(1400px 宽) | 短内容垂直堆叠→自然填满高度 |
| 3 项 | 图标 + 名称 + 2–3 行 | 3 列卡片(最小 1100px 宽) | 有足够内容填充卡片高度而无空洞 |
| 2 项 | 内容较多 | 并排大卡片或分屏 | |
| 1 项 | 任何内容 | 全屏主视觉 |
如果内容的自然布局在门控 1 之后使得任何轴低于阈值——那么该布局就是错误的。在此表中选择另一行。
永远不要根据美学或默认直觉来选择布局。内容适配优先。美学次之。
门控 3——内容密度限制
每项最多:图标 + 名称 + 1 个短行。硬性停止。 没有项目符号。没有子部分。没有段落。没有第二行描述。
一个观众大约有 ~5 秒的时间看一个场景。他们读 1 行。他们不会读 4 个要点。
如果内容感觉单薄,你想添加更多来填充空间——那是门控 2 在告诉你布局形状不对,而不是内容缺失。
使用 大小(更大的字体、更大的图标、项目之间更大的间距)来填充空白空间——永远不要使用更多内容。
通用场景规则
-
垂直对齐——
justifyContent: "center"+marginTop: -40到-60(略微高于真正中心 = 现代、高级感。NOT 顶部对齐加内边距。)❌ 错误——顶部对齐,底部留下空白:
<AbsoluteFill style={{ display: "flex", flexDirection: "column", paddingTop: 110 }}> <h1>主标题</h1> <p>副文本</p> {/* 底部大片空白 */} </AbsoluteFill>✅ 正确——垂直居中,略微高于中点:
<AbsoluteFill style={{ display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", marginTop: -50 }}> <h1>主标题</h1> <p>副文本</p> {/* 内容填满画面——无死区 */} </AbsoluteFill> -
画布填充——在编写此场景之前运行预布局门控。 门控 1 同时检查宽度(
dead_side ≤ 300px)和高度(fill_h ≥ 0.80)。门控 2 从决策表中选择布局形状。门控 3 限制每项内容。所有三个门控都位于上面的预布局门控部分——它们取代了之前仅检查宽度的规则。 -
标题 z-index——在标题容器上始终使用
zIndex: 20,以便 Mockup 永远不会与文本重叠 -
标题溢出——标题文本上始终使用
whiteSpace: "nowrap"(防止在 Mockup 后换行) -
不要对 AbsoluteFill 使用
overflow: hidden——会裁剪光标和 SVG 元素。移除它或将光标放在外部。 -
15 帧保持——每个场景在其结束处必须至少有 15 帧完全可见的保持,然后才触发过渡。在传出场景仍然播放动画时,绝不要开始过渡。
-
字体——全程使用一种一致的字体。推荐:通过
@remotion/google-fonts使用 Plus Jakarta Sans。最大可用粗细:800(将fontWeight: 900写作 800)。 -
颜色——最多 2–3 种品牌颜色。一种深色背景,一种主要强调色,文字使用白色或柔和白色。所有场景保持一致。
排版层级
// 超级标题/标签药丸
{ fontSize: 20, letterSpacing: "4px", textTransform: "uppercase", fontWeight: 600 }
// 主标题
{ fontSize: 112, fontWeight: 800 } // 范围:100–160px
// 副文本/描述
{ fontSize: 36, opacity: 0.7 } // 范围:32–40px
// 信任行/小徽章
{ fontSize: 26, opacity: 0.5 } // 范围:24–28px
字体设置
import { loadFont } from "@remotion/google-fonts/PlusJakartaSans";
// 在顶层调用——必须在渲染前加载
const { fontFamily } = loadFont("normal", {
weights: ["400", "600", "800"],
subsets: ["latin"],
});
对于其他字体:通过 @remotion/google-fonts/FontName 安装,或将 .woff2 放在 public/ 并使用 @remotion/fonts 的 loadFont。
场景结构模板
每个场景遵循此固定图层顺序(从下到上):
<AbsoluteFill style={{ background: brandBg }}