# Remotion Multi-Track Editor 基于 **Remotion + React 19 + Vite + Zustand** 的多轨视频编辑器,仿剪映风格的深色 UI。编辑器作为普通 React 应用运行,预览由 `@remotion/player` 实时渲染,导出通过 Vite 中间件调用 `@remotion/renderer` 完成,并支持把字幕以 SRT 软字幕轨道的形式封装进 MP4(剪映可识别)。
演示动画
演示动画
## 特性 - **多轨时间线**:背景轨 + 叠加轨,支持增加 / 删除 / 锁定 / 静音轨道 - **拖拽编辑**:拖动片段调整起始位置与时长,鼠标移出轨道区域自动结束拖拽,无需点击释放 - **多轨对齐吸附**:在 6px 范围内检测对齐点并显示金色垂直参考线 - **组件库**:素材库与字幕库独立分页,点击右下角加号即可添加到轨道 - **实时预览**:`@remotion/player` 渲染当前时间线,所见即所得 - **属性面板**:根据组件 `propSchema` 自动生成文本 / 颜色 / 数字 / 多行文本输入 - **MP4 导出**:异步多任务导出,进度轮询,结果直接下载 - **字幕导出**: - 一键导出 `.srt` 字幕文件(剪映兼容) - 导出 MP4 时自动用 ffmpeg 将字幕以 `mov_text` 软字幕轨道嵌入视频 ## 技术栈 | 分类 | 选型 | | --- | --- | | 视频框架 | Remotion 4.0.484 | | UI 框架 | React 19.2.3 | | 构建工具 | Vite 8 | | 状态管理 | Zustand 5 | | 样式 | Tailwind CSS 4 | | 类型 / 校验 | TypeScript 5.9.3 + Zod 4 | | 字幕封装 | ffmpeg(`mov_text`) | ## 快速开始 ```console pnpm i pnpm run dev ``` 打开 http://localhost:5173 即可使用编辑器。 > 导出 MP4 依赖本机 ffmpeg。请修改 [vite.config.ts](./vite.config.ts) 中的 `FFMPEG_PATH` 为你的 `ffmpeg.exe` 实际路径。 ## 可用脚本 | 命令 | 说明 | | --- | --- | | `pnpm run dev` | 启动 Vite 开发服务器(编辑器 + 导出 API,端口 5173) | | `pnpm run build` | `remotion bundle` 打包 Remotion 入口 | | `pnpm run studio` | 启动 Remotion Studio(独立调试 Composition) | | `pnpm run lint` | ESLint + `tsc` 类型检查 | | `pnpm run upgrade` | 升级 Remotion 全家桶 | ## 目录结构 ``` my-video/ ├─ public/ │ ├─ favicon.svg # 站点图标 │ └─ exports/ # 导出 MP4 输出目录 ├─ src/ │ ├─ main.tsx # 编辑器入口(挂载 EditorApp) │ ├─ Root.tsx # Remotion Composition 注册 │ ├─ index.ts # Remotion 入口(供 bundle) │ ├─ index.css # 全局样式 │ ├─ components/ # Remotion 可复用组件 │ │ ├─ HelloWorld/ # Logo + Title + Subtitle 组合示例 │ │ ├─ ClaudeType.tsx # Claude 风格 AI 聊天打字动画 │ │ ├─ SubtitleClip.tsx # 单条字幕 │ │ ├─ SubtitleTrack.tsx # 编程式批量字幕轨道 │ │ ├─ Wechat2DRender.tsx # 微信风格二维码 │ │ └─ RulerProgressRender.tsx # 标尺进度条 │ └─ editor/ # 编辑器核心 │ ├─ editor.tsx # 主布局(顶栏 + 库 + 预览 + 属性 + 时间线) │ ├─ component-library.tsx # 组件库面板(素材库 / 字幕库) │ ├─ preview.tsx # @remotion/player 预览 │ ├─ timeline.tsx # 多轨时间线 + 拖拽 + 吸附 │ ├─ properties.tsx # 属性面板 │ ├─ registry.tsx # 组件注册表 │ ├─ renderer.tsx # tracks/clips → React 树(预览 + 导出共用) │ ├─ store.ts # Zustand 编辑器状态 │ ├─ export-store.ts # 导出任务状态 │ ├─ types.ts # Clip / Track / EditorState 类型 │ └─ nanoid.ts # id 生成 ├─ vite.config.ts # Vite + 导出 API 中间件 ├─ remotion.config.ts # Remotion 配置 └─ eslint.config.mjs # ESLint flat config ``` ## 已注册组件 | key | 名称 | 说明 | | --- | --- | --- | | `helloworld` | Hello World | Logo + 标题 + 字幕组合示例 | | `subtitle` | 字幕 | 单条字幕,可调文本 / 颜色 / 字号 / 位置 | | `subtitleTrack` | 字幕轨道 | 编程式批量字幕,每行 `起始秒-结束秒: 文本` | | `claudeType` | Claude 打字机 | Claude 风格 AI 聊天打字动画 | | `wechat2d` | 微信二维码 | 微信风格二维码 + 扫描线 + 呼吸光效 | | `rulerProgress` | 标尺进度条 | 底部时间标尺进度条 + 播放头 | 新增组件只需在 [src/components/](./src/components/) 编写 Remotion 组件,再到 [src/editor/registry.tsx](./src/editor/registry.tsx) 的 `registry` 数组里登记 `key / component / defaultProps / propSchema` 即可。 ## 工作原理 ### 预览 编辑器不使用 Remotion Studio,而是通过 Vite 直接运行普通 React 应用。`CompositionRenderer`([renderer.tsx](./src/editor/renderer.tsx))读取 store 中的 `tracks / clips`,把每个轨道渲染为一层 `AbsoluteFill`,每个片段用 `` 包裹对应组件,最后交给 `@remotion/player` 播放。 ### 导出 Vite 开发服务器在 [vite.config.ts](./vite.config.ts) 中注册了两个中间件: - `POST /api/export/start` — 接收 `inputProps`(tracks/clips),启动异步渲染,返回 `jobId` - `GET /api/export/status?jobId=xxx` — 轮询进度,完成后返回下载 URL 渲染流程: 1. `@remotion/bundler` 打包 `src/index.ts`(首次约 10–30s,后续复用缓存) 2. `selectComposition` 选中 `EditorExport` 并按 `totalDuration` 覆盖时长 3. `renderMedia` 输出 H264 MP4 到 `public/exports/.mp4` 4. 若时间线上有字幕,生成 SRT 并用 `ffmpeg -c:s mov_text` 封装进 MP4 ### 字幕导出 - **SRT 文件**:编辑器顶栏「导出 SRT」按钮,扫描所有 `subtitle` / `subtitleTrack` 片段,按起始帧排序后输出 `.srt` - **MP4 内嵌字幕**:导出 MP4 时自动执行,剪映可直接识别为软字幕轨道 ## 工程约束 - 字幕组件必须支持 SRT 格式导出,以确保剪映兼容性 - 拖拽片段时,鼠标移出轨道区域需自动结束拖拽,无需点击释放 - Remotion 组件中使用 `interpolate` 函数时,`inputRange` 必须严格单调递增 - 直接使用 koa-connect 包装 Express 中间件会导致 ctx 泄漏,需进行原生 Koa 重写 ## 许可 UNLICENSED(私有权)。