Skip to main content

从数据集中以编程方式渲染视频

您可以使用 Remotion 进行批量渲染,根据数据集创建许多视频。在以下示例中,我们将把一个 JSON 数据集转换为一系列视频。

我们将从创建一个空白的 Remotion 项目开始:

bash
npm init video --blank
bash
npm init video --blank

示例数据集

JSON 是在 Remotion 中导入的最方便的格式。如果您的数据集是以不同格式存在的,您可以使用 NPM 上的许多可用库之一进行转换。

my-data.ts
ts
export const data = [
{
name: "React",
repo: "facebook/react",
logo: "https://upload.wikimedia.org/wikipedia/commons/a/a7/React-icon.svg",
},
{
name: "Remotion",
repo: "remotion-dev/remotion",
logo: "https://github.com/remotion-dev/logo/raw/main/withouttitle/element-0.png",
},
];
my-data.ts
ts
export const data = [
{
name: "React",
repo: "facebook/react",
logo: "https://upload.wikimedia.org/wikipedia/commons/a/a7/React-icon.svg",
},
{
name: "Remotion",
repo: "remotion-dev/remotion",
logo: "https://github.com/remotion-dev/logo/raw/main/withouttitle/element-0.png",
},
];

示例组件

此组件将使用 Remotion 动画化标题、副标题和图像。将 src/Composition.tsx 文件的内容替换为以下内容:

Composition.tsx
tsx
import React from "react";
import {
AbsoluteFill,
Img,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
} from "remotion";
type Props = {
name: string;
logo: string;
repo: string;
};
export const MyComposition: React.FC<Props> = ({ name, repo, logo }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scale = spring({
fps,
frame: frame - 10,
config: {
damping: 100,
},
});
const opacity = interpolate(frame, [30, 40], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const moveY = interpolate(frame, [20, 30], [10, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<AbsoluteFill
style={{
scale: String(scale),
backgroundColor: "white",
fontWeight: "bold",
justifyContent: "center",
alignItems: "center",
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 20,
}}
>
<Img
src={logo}
style={{
height: 80,
}}
/>
<div
style={{
display: "flex",
flexDirection: "column",
}}
>
<div
style={{
fontSize: 40,
transform: `translateY(${moveY}px)`,
lineHeight: 1,
}}
>
{name}
</div>
<div
style={{
fontSize: 20,
opacity,
lineHeight: 1.25,
}}
>
{repo}
</div>
</div>
</div>
</AbsoluteFill>
);
};
Composition.tsx
tsx
import React from "react";
import {
AbsoluteFill,
Img,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
} from "remotion";
type Props = {
name: string;
logo: string;
repo: string;
};
export const MyComposition: React.FC<Props> = ({ name, repo, logo }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scale = spring({
fps,
frame: frame - 10,
config: {
damping: 100,
},
});
const opacity = interpolate(frame, [30, 40], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const moveY = interpolate(frame, [20, 30], [10, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<AbsoluteFill
style={{
scale: String(scale),
backgroundColor: "white",
fontWeight: "bold",
justifyContent: "center",
alignItems: "center",
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 20,
}}
>
<Img
src={logo}
style={{
height: 80,
}}
/>
<div
style={{
display: "flex",
flexDirection: "column",
}}
>
<div
style={{
fontSize: 40,
transform: `translateY(${moveY}px)`,
lineHeight: 1,
}}
>
{name}
</div>
<div
style={{
fontSize: 20,
opacity,
lineHeight: 1.25,
}}
>
{repo}
</div>
</div>
</div>
</AbsoluteFill>
);
};
Remotion
remotion-dev/remotion
0:00 / 0:02

编写脚本

为了渲染我们的视频,我们首先需要使用 Webpack 打包我们的项目并准备好进行渲染。 这可以通过使用 @remotion/bundler 包中的 bundle() 函数来完成。如果有 webpack 覆盖,请确保在捆绑时包含 webpack 覆盖。

ts
import { bundle } from "@remotion/bundler";
import { webpackOverride } from "./webpack-override";
 
const bundleLocation = await bundle({
entryPoint: "./src/index.ts",
// If you have a webpack override, don't forget to add it
webpackOverride: webpackOverride,
});
ts
import { bundle } from "@remotion/bundler";
import { webpackOverride } from "./webpack-override";
 
const bundleLocation = await bundle({
entryPoint: "./src/index.ts",
// If you have a webpack override, don't forget to add it
webpackOverride: webpackOverride,
});

渲染视频

导入数据集并循环遍历每个条目。

使用 selectComposition() 获取每个渲染的组合元数据。您可以使用 calculateMetadata() 函数基于数据条目参数化组合的持续时间、宽度和高度。

触发使用 renderMedia() 并将数据条目作为 inputProps 传递。这将将对象作为 React props 传递给上面的组件。

ts
import { renderMedia, selectComposition } from "@remotion/renderer";
import { data } from "./dataset";
 
for (const entry of data) {
const composition = await selectComposition({
serveUrl: bundleLocation,
id: compositionId,
inputProps: entry,
});
 
await renderMedia({
composition,
serveUrl: bundleLocation,
codec: "h264",
outputLocation: `out/${entry.name}.mp4`,
inputProps: entry,
});
}
ts
import { renderMedia, selectComposition } from "@remotion/renderer";
import { data } from "./dataset";
 
for (const entry of data) {
const composition = await selectComposition({
serveUrl: bundleLocation,
id: compositionId,
inputProps: entry,
});
 
await renderMedia({
composition,
serveUrl: bundleLocation,
codec: "h264",
outputLocation: `out/${entry.name}.mp4`,
inputProps: entry,
});
}
note

不建议同时渲染多个视频。

完整脚本

render.mjs
ts
import { selectComposition, renderMedia } from "@remotion/renderer";
import { webpackOverride } from "./webpack-override";
import { bundle } from "@remotion/bundler";
import { data } from "./dataset";
 
const compositionId = "MyComp";
 
const bundleLocation = await bundle({
entryPoint: "./src/index.ts",
// If you have a webpack override in remotion.config.ts, pass it here as well.
webpackOverride: webpackOverride,
});
 
for (const entry of data) {
const composition = await selectComposition({
serveUrl: bundleLocation,
id: compositionId,
inputProps: entry,
});
 
await renderMedia({
composition,
serveUrl: bundleLocation,
codec: "h264",
outputLocation: `out/${entry.name}.mp4`,
inputProps: entry,
});
}
render.mjs
ts
import { selectComposition, renderMedia } from "@remotion/renderer";
import { webpackOverride } from "./webpack-override";
import { bundle } from "@remotion/bundler";
import { data } from "./dataset";
 
const compositionId = "MyComp";
 
const bundleLocation = await bundle({
entryPoint: "./src/index.ts",
// If you have a webpack override in remotion.config.ts, pass it here as well.
webpackOverride: webpackOverride,
});
 
for (const entry of data) {
const composition = await selectComposition({
serveUrl: bundleLocation,
id: compositionId,
inputProps: entry,
});
 
await renderMedia({
composition,
serveUrl: bundleLocation,
codec: "h264",
outputLocation: `out/${entry.name}.mp4`,
inputProps: entry,
});
}

运行脚本

bash
node render.mjs
bash
node render.mjs

要使用 TypeScript,请将文件重命名为 render.ts,从 npm 安装 ts-node 并运行 ts-node render.ts。如果出现错误,请将异步代码包装在异步函数中并调用它。

从 CSV 数据集渲染视频

使用类似 csv2json 的软件包将数据集转换为 JSON。

参见