Skip to main content

构建一个基于时间轴的视频编辑器

本文件从高层次描述了如何将 Remotion Player 与时间轴同步。
请阅读本文件以获得构建具有以下特性的视频编辑器的指导:

🌐 This document describes on a high-level how the Remotion Player can be synchronized with a timeline.
Read this document for guidance on building a video editor with the following characteristics:

  • 多个相互叠加的音轨
  • 物品可以任意放置在轨道上
  • 项目可以有不同的类型(例如视频、音频、文本等)

获取 <Timeline> 组件

🌐 Get the <Timeline> component

我们提供一个可复制粘贴的 <Timeline> 组件,它遵循 Remotion 的最佳实践,并且已经处理了缩放功能。 如果你想节省时间并提前开始,你可以在 Remotion 商店购买它

🌐 We offer a copy-pasteable <Timeline> component that follows Remotion's best practices and also already handles zoom.
If you want to save time and get a head start, you can purchase it in the Remotion Store.

你也可以自己构建时间轴组件。 以下步骤将使用我们构建时间轴组件时使用的相同方法。

🌐 You can also build your own timeline component.
The following steps will use the same approach we used to build our Timeline component.

观看“在 React 中构建视频编辑器”演讲

🌐 Watch the "Build a video editor in React" talk

观看 Jonny Burger(Remotion 的创始人)关于“在 React 中构建视频编辑器”的演讲 这里。 你将在短短 30 分钟内获得如何构建时间轴、画布、字幕和导出功能的概述。

🌐 Watch the talk "Build a video editor in React" by Jonny Burger, the creator of Remotion here.
You'll receive an outline of how to build a timeline, canvas, captioning and exporting functionality in just 30 minutes.

创建你自己的时间线

🌐 Build your own timeline



定义一个 TypeScript 类型 Item 来定义不同的项目类型。创建另一个类型来定义 Track 的结构:

types.ts
type BaseItem = { from: number; durationInFrames: number; id: string; }; export type SolidItem = BaseItem & { type: 'solid'; color: string; }; export type TextItem = BaseItem & { type: 'text'; text: string; color: string; }; export type VideoItem = BaseItem & { type: 'video'; src: string; }; export type Item = SolidItem | TextItem | VideoItem; export type Track = { name: string; items: Item[]; };

创建一个可以渲染曲目列表的组件。

remotion/Main.tsx
import type {Track, Item} from './types'; import React from 'react'; import {AbsoluteFill, Sequence, OffthreadVideo} from 'remotion'; const ItemComp: React.FC<{ item: Item; }> = ({item}) => { if (item.type === 'solid') { return <AbsoluteFill style={{backgroundColor: item.color}} />; } if (item.type === 'text') { return <h1>{item.text}</h1>; } if (item.type === 'video') { return <OffthreadVideo src={item.src} />; } throw new Error(`Unknown item type: ${JSON.stringify(item)}`); }; const Track: React.FC<{ track: Track; }> = ({track}) => { return ( <AbsoluteFill> {track.items.map((item) => { return ( <Sequence key={item.id} from={item.from} durationInFrames={item.durationInFrames}> <ItemComp item={item} /> </Sequence> ); })} </AbsoluteFill> ); }; export const Main: React.FC<{ tracks: Track[]; }> = ({tracks}) => { return ( <AbsoluteFill> {tracks.map((track) => { return <Track track={track} key={track.name} />; })} </AbsoluteFill> ); };
tip

在 CSS 中,呈现在底部的元素会出现在顶部。参见:图层

保持一个状态,其中每个状态包含一个项目数组。

渲染一个 <Player /> 组件,并将 tracks 作为 inputProps 传递。

🌐 Render a <Player /> component and pass the tracks as inputProps.

Editor.tsx
import React, {useMemo, useState} from 'react'; import {Player} from '@remotion/player'; import type {Item} from './types'; import {Main} from './remotion/Main'; type Track = { name: string; items: Item[]; }; export const Editor = () => { const [tracks, setTracks] = useState<Track[]>([ {name: 'Track 1', items: []}, {name: 'Track 2', items: []}, ]); const inputProps = useMemo(() => { return { tracks, }; }, [tracks]); return ( <> <Player component={Main} fps={30} inputProps={inputProps} durationInFrames={600} compositionWidth={1280} compositionHeight={720} /> </> ); };

构建时间线组件:你现在可以访问 tracks 状态,并可以使用 setTracks 函数进行更新。

我们目前不提供如何构建时间轴组件的示例,因为每个人的需求和样式偏好都不同。

🌐 We do not currently provide samples how to build a timeline component, since everybody has different needs and styling preferences.

一个有主见的示例实现可以在 Remotion 商店 购买。

🌐 An opinionated sample implementation is available for purchase in the Remotion Store.

remotion/Timeline.tsx
const Editor: React.FC = () => { const [tracks, setTracks] = useState<Track[]>([ {name: 'Track 1', items: []}, {name: 'Track 2', items: []}, ]); const inputProps = useMemo(() => { return { tracks, }; }, [tracks]); return ( <> <Player component={Main} fps={30} inputProps={inputProps} durationInFrames={600} compositionWidth={1280} compositionHeight={720} /> <Timeline tracks={tracks} setTracks={setTracks} /> </> ); };

另请参阅

🌐 See also