动画信息图 是做什么的?
构建一个经过设计的信息图——图标、几个关键数字、简短文本和具有真实视觉层次的简单形状——然后按序列动画呈现,让每个元素都有其展示时刻。工艺首先是构图,其次是编排:一个本身就能传达信息的静态布局,通过错落有致、指导视线精准移动的级联方式进行揭示。
何时使用
- 混合布局解释器:“X 分 3 步”、“工作原理”、“数字说话”、统计卡片网格。
- 图标 + 统计 + 标签 + 连接线作为单个设计场景的序列揭示。
- 象形图/图标动画、计数的关键数字、步骤之间的流程连接线。
本技能专精构图信息图。对于内容即图表本身(条形图竞赛、折线/面积图动画、CSV 驱动的图表),请使用 chart-animation——并将其图表作为信息图内的一个整体块进行构图,而不是重新构建。
唯一规则:先设计静态帧
一个优秀的动画信息图首先是一个优秀的静态信息图,只是恰好会动。在添加任何一个关键帧之前,先布局并平衡整个构图——每个图标、数字和标签都位于其最终位置。动画随后仅控制何时显示每个已放置好的元素;它永远不决定布局。这可以防止最常见的失败:元素飞入从未设计过的位置,导致最终定格帧看起来像偶然拼凑。
严格限定范围:一个核心见解,3-5 个支持性数据点,时长 30-90 秒。超过五个相互竞争的元素会使级联效果显得混乱。
视觉层次 → 揭示顺序
揭示顺序就是层次。视线跟随出现顺序,因此元素应按重要性顺序(而非布局顺序)进行动画。将每个元素映射到一个层级,然后逐层揭示。
| 层级 | 元素 | 揭示 | 运动 |
|---|---|---|---|
| 1 | 章节标题 / 核心见解 | 第一个,单独出现 | 淡入 + 轻微上升 |
| 2 | 图标(锚定每个项目) | 每个项目都出现,引领其组 | 弹出 / 缩放过冲 |
| 3 | 关键数字(计数器) | 紧随其图标之后 | 计数,减速缓出 |
| 4 | 标签 / 说明文字 | 在数字下方稳定下来 | 淡入,无强烈运动 |
| 5 | 连接线 / 流程线 | 在项目完成时连接它们 | 绘制(路径长度) |
使用大小、粗细和颜色实现静态层次;使用时机和运动实现动画层。一次只关注一个重点——切勿同时计数两个数字。
交错:级联
以固定的延迟(交错)揭示同组元素,使组读起来像一个序列,而非一次闪现。驱动每个元素都基于当前帧作为纯函数——绝不用墙上时钟时间或库的动画循环——从而确保渲染是确定性的。
import { useCurrentFrame, spring, useVideoConfig } from "remotion";
const STAGGER = 8; // 同组元素之间的帧数(30fps 约 0.27 秒)
function Item({ index, children }) {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const local = frame - index * STAGGER; // 此元素自身的时钟
const enter = spring({ frame: local, fps, config: { damping: 14, mass: 0.6 } });
return (
<div style={{ opacity: enter, transform: `translateY(${(1 - enter) * 24}px)` }}>
{children}
</div>
);
}
6-10 帧的交错感觉利落;超过约 15 帧则显得拖沓。在所有同组元素中使用相同的入场曲线,使级联读起来像一种自信的节奏,而非噪点。
图标动画:弹出
图标锚定每个项目,因此赋予它们最强的入场效果——一种缩放过冲(“弹出”),然后稳定下来。使用低阻尼的弹簧实现弹跳;切勿使用线性缩放(读起来像机械运动)。
const pop = spring({ frame: local, fps, config: { damping: 10, stiffness: 180, mass: 0.5 } });
// pop 超过 1 然后稳定下来 → 生动活泼
<g transform={`scale(${pop})`} style={{ transformOrigin: "center" }}>{icon}</g>
对于象形图填充(例如“10 人中有 7 人”),以相同的交错方式揭示单元,并使用遮罩裁剪部分单元,而不是对其进行缩放。
关键数字:稳定下来的计数器
对底层数字进行插值,应用缓动函数,然后在渲染时格式化。两个必须:在格式化前进行四舍五入,并使用 tabular-nums 使布局在数字变化时不抖动。(与 chart-animation 共享的工艺——参见其计数器部分了解货币/百分比变体。)
import { interpolate, Easing } from "remotion";
const raw = interpolate(frame - delay, [0, 30], [0, 1287], {
extrapolateLeft: "clamp", extrapolateRight: "clamp",
easing: Easing.out(Easing.cubic), // “稳定在一个数字上”
});
const label = new Intl.NumberFormat("en-US").format(Math.round(raw)); // "1,287"
// CSS: font-variant-numeric: tabular-nums; line-height: 1;
标题数字的缓动输出入读起来像到达;线性读起来像机械的里程表。在下一个项目开始前,每个数字保持约 0.5 秒。
连接线与流程
连接线将卡片网格转变为流程。通过绘制路径来为线/箭头添加动画,同步动画使其恰好在其指向的项目完成时结束——这种运动表示“现在看这里,接下来”。
const pathRef = useRef();
const len = pathRef.current?.getTotalLength?.() ?? 0;
const draw = interpolate(frame - delay, [0, 18], [0, 1], { extrapolateRight: "clamp" });
<path ref={pathRef} d="M40,0 C40,30 40,30 40,60" fill="none" stroke="#444" strokeWidth={3}
strokeDasharray={len} strokeDashoffset={len * (1 - draw)} />
在源项目出现之后、目标项目出现之前的间隙中,在连接的两个项目之间绘制连接线——这条线将视线引导至下一个揭示。
节拍控制
按元素预算时间,而非按秒打磨。观众一次只能跟踪一个移动的物体。
| 节拍 | 预算 |
|---|---|
| 标题停留(让其稳定) | 0.8–1.2 秒 |
| 每个项目(图标弹出 → 计数器 → 标签) | 1.2–2.0 秒 |
| 连接线绘制 | 0.4–0.6 秒,与下一个项目重叠 |
| 最终完整定格(可截屏) | 2–3 秒 |
以完整的静态信息图保持静止 2-3 秒结束——要点是最终组装的帧,因此让它稳定下来,不要有运动干扰。
输出检查清单
- 静态布局在任何关键帧之前就已设计和平衡完毕;最终的定格帧看起来是有意为之。
- 一个核心见解,3-5 个数据点,30-90 秒。
- 揭示顺序遵循层次(标题 → 图标 → 数字 → 标签 → 连接线),而非布局顺序。
- 同组元素以一致的 6-10 帧交错和一条入场曲线级联显示。
- 图标以弹簧过冲效果弹出;数字以缓动效果计数并配合
tabular-nums;一次只有一个数字进行动画。 - 连接线逐步绘制,将视线引导至下一个揭示。
- 每个值都是
useCurrentFrame()的纯函数;无库定时器。 - 最终合成的帧保持 ≥2 秒。
交付与验证(渲染静态图像 → MP4)
打包辅助脚本(
scripts/):使用scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png平铺您的静态图像,然后使用scripts/probe-mp4.sh out.mp4 [WxH] [fps]断言编码。请参阅scripts/README.md。
Remotion 是帧确定性的——每个图标弹出、计数器值和连接线绘制都是 useCurrentFrame() 的纯函数,因此您可以在无需寻道工具的情况下无头渲染任何精确帧。信息图携带关键数字,因此静态图像检查可以在您浪费一次编码之前捕获错误的统计数据或不在画布内的交错。
输出约定:
- 一个 Remotion 项目,包含已注册的场景(
<Composition>+ zodschema+defaultProps),所有运动由帧驱动(无定时器 /Date.now()/Math.random())。 - 可交付成果 = 渲染的
out/*.mp4(以及项目,以便用户可以使用新的统计数据/图标重新渲染)。 - 时长依赖于数据(N 个项目 × 每项目预算)?在
calculateMetadata中计算,而非手动。
验证循环——渲染静态图像 → 检查 → 编码。 首先渲染单帧(便宜,无需视频编码),然后在布局和数字正确后仅编码一次。
# 1. 帧精确的静态图像——使用 SHIPPED props。选择捕捉每个层级的帧:
npx remotion still Infographic out/f-title.png --frame=20 --props='{"items":[...]}' # 标题 + 首个弹出
npx remotion still Infographic out/f-mid.png --frame=90 --props='{"items":[...]}' # 中间级联
npx remotion still Infographic out/f-end.png --frame=149 --props='{"items":[...]}' # 最终合成定格(= durationInFrames - 1)
# 2. 检查每个 PNG——正确性(每个关键数字精确,标签/说明文字正确,图标为正确的图形)
# 和瑕疵(文本溢出,图标不在画布内,安全区裁剪,缺少字体,连接线指向
# 错误项目,层级应该稳定时元素半揭示)。
# 3. 仅在静态图像检查通过后,进行编码:
npx remotion render Infographic out/infographic.mp4 --props='{"items":[...]}'
- 使用
npx remotion compositions读取durationInFrames/fps;最终合成定格是关键帧——验证其看起来是有意为之,而非级联中途。 - 数据驱动 / 批量:在通过静态图像批量渲染所有变体之前,验证一个代表性 props 集(统计数据 + 标签)——一次捕获计数器或布局错误,而非 N 次。
- 免费获取 README 演示 GIF:
npx remotion render Infographic out/demo.gif --codec=gif。
在完成之前:
npx remotion still在标题、中级联和最终定格处均能干净渲染——无错误,无缺失字体/图标。- 每个关键数字都是精确的(已四舍五入,
tabular-nums),且每个元素在每一帧都位于安全区内。 - 仅帧驱动——无
Date.now()/Math.random()/ 库定时器(确定性在 CI 中保持有效)。 - 已发布的 props 正确渲染(不仅仅是
defaultProps)——正确的统计数据、正确的图标、正确的标签。 - 完整 MP4 已编码并播放;(可选)为 README 渲染了 GIF。
参考文件
references/sequenced-infographic.md— 一个完整的可运行 Remotion 场景:一个 3 步“工作原理”信息图,包含交错的图标弹出、缓动计数的统计信息、绘制的连接线、主时机图以及使其数据驱动(模板 × 数据)的 prop 模式。包含一个无依赖的内联 SVG 变体,适用于非 Remotion 环境。