Remotion 教程 11:做数据可视化视频时,只让 D3 负责计算
D3 很强,但在 Remotion 里不能照搬网页图表写法。最重要的边界是:
D3 负责 scale、shape、数据映射
Remotion 负责 frame 动画为什么不用 D3 transition
网页 D3 示例里经常会看到:
selection.transition().duration(800)这类写法适合网页运行时动画,但不适合作为 Remotion 视频主动画。它的问题和 CSS transition、Motion runtime 动画类似:动画过程由运行时推进,而不是由 frame 直接推导。
在 Remotion 里更稳的写法是:
数据 -> D3 scale -> Remotion frame -> style / svg 属性D3 负责 scale
例如柱状图需要两个映射:
const x = scaleBand()
.domain(chartData.map((item) => item.label))
.range([0, 980])
.padding(0.24);
const y = scaleLinear().domain([0, 100]).range([0, 420]);scaleBand() 负责计算每个柱子的横向宽度。
scaleLinear() 负责把数据值转换成像素高度。
这就是 D3 在 Remotion 里的推荐角色:负责数学映射。
Remotion 负责动画
每根柱子出现时,使用 Remotion 的 interpolate():
const progress = interpolate(
frame,
[36 + index * 8, 72 + index * 8],
[0, 1],
{
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
},
);这段代码让每根柱子错开出现:
第 1 根:36 -> 72 帧
第 2 根:44 -> 80 帧
第 3 根:52 -> 88 帧然后把进度乘到柱子高度上:
const height = y(item.value) * progress;这样就能得到稳定的逐帧动画。
完整最小示例
下面是一个 D3 计算柱宽和高度映射、Remotion 控制动画的柱状图。
import React from "react";
import { scaleBand, scaleLinear } from "d3";
import { AbsoluteFill, interpolate, useCurrentFrame } from "remotion";
const chartData = [
{ label: "脚本", value: 72 },
{ label: "字幕", value: 88 },
{ label: "素材", value: 64 },
{ label: "渲染", value: 96 },
];
export const DataVideo: React.FC = () => {
const frame = useCurrentFrame();
const x = scaleBand()
.domain(chartData.map((item) => item.label))
.range([0, 980])
.padding(0.24);
const y = scaleLinear().domain([0, 100]).range([0, 420]);
return (
<AbsoluteFill
style={{
backgroundColor: "#f8fafc",
padding: 96,
fontFamily: "Inter, sans-serif",
}}
>
<h1 style={{ margin: 0, fontSize: 72 }}>
D3 计算图形,Remotion 控制动画
</h1>
<div
style={{
marginTop: 120,
height: 520,
display: "flex",
alignItems: "flex-end",
gap: 24,
}}
>
{chartData.map((item, index) => {
const progress = interpolate(
frame,
[36 + index * 8, 72 + index * 8],
[0, 1],
{
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
},
);
const height = y(item.value) * progress;
return (
<div
key={item.label}
style={{
width: x.bandwidth(),
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<div
style={{
width: "100%",
height,
borderRadius: 8,
backgroundColor: "#16a34a",
}}
/>
<div style={{ marginTop: 18, fontSize: 30, fontWeight: 800 }}>
{item.label}
</div>
</div>
);
})}
</div>
</AbsoluteFill>
);
};D3 适合什么视频
优先考虑 D3 的场景:
- 柱状图、折线图、面积图。
- 排名变化视频。
- 数据报告自动生成。
- 地图、路径、弧线、力导向等图形计算。
- 需要 scale、axis、shape、interpolate 的图表。
如果只是几个固定数字卡片,不需要 D3。直接用 interpolate() 和普通布局就够了。
和 zod、Renderer 的关系
数据视频通常会进入批量生成流程。建议把输入数据做成明确结构,并用 zod 校验:
type ChartItem = {
label: string;
value: number;
};真实项目里可以把 chartData 替换成接口返回的数据,再用 @remotion/renderer 批量生成日报、周报、投放报告或业务汇总视频。
示例项目位置
这篇文章对应示例项目里的:
Composition: LessonDataVideo
文件: src/lessons/DataVideo.tsx
数据: src/course/mockData.ts运行示例项目后,可以尝试:
- 修改
chartData数值。 - 把
[36 + index * 8, 72 + index * 8]改成[20 + index * 6, 64 + index * 6]。 - 观察柱子是否更早出现、节奏是否更紧。
下一篇回到完整工作流:把脚本、音频、字幕、素材和模板串成 AI 口播短视频。