Remotion LabRemotion Lab
資源錄製器模板

錄製器模板

使用 Remotion 的 Recorder 模板快速建立螢幕錄製加工具,結合程式碼高亮、自動字幕與品牌化輸出。

錄製器模板

Recorder Template 是 Remotion 為技術教學影片、產品展示和開發者內容創作設計的起始模板。它將螢幕錄影與 Remotion 的後製能力結合,讓你可以用程式碼控制的方式,快速為錄製影片加上字幕、品牌識別、程式碼高亮框和章節標記。

適用場景

  • 技術教學影片:為程式碼教學自動加上語法高亮標注
  • 產品 Demo:在螢幕錄製上疊加說明文字和品牌元素
  • 線上課程:自動生成字幕,降低後製門檻
  • 開發者內容:用統一風格批次製作技術短影音

模板功能

  • 螢幕錄製整合:匯入 mp4 錄製檔,自動對齊時間軸
  • 自動字幕:整合 Whisper 轉錄,逐字同步顯示
  • 程式碼高亮框:在特定時間點疊加語法高亮的程式碼片段
  • 章節標記:自動在影片底部顯示目前章節標題
  • 品牌化背景:客製化背景、Logo 和色彩主題
  • 一鍵渲染:輸出適合 YouTube、Twitter、LinkedIn 的影片規格

快速開始

npx create-video@latest --template recorder
cd my-recorder-video
npm install

設定環境變數:

# .env.local
OPENAI_API_KEY=your-openai-api-key

工作流程

  1. 將你的螢幕錄製影片放到 public/ 目錄
  2. 執行轉錄腳本生成字幕
  3. src/config.ts 中設定章節和程式碼高亮
  4. 用 Remotion Studio 預覽效果
  5. 渲染最終影片
# 1. 轉錄字幕
npm run transcribe -- --input public/my-recording.mp4
 
# 2. 預覽
npm run dev
 
# 3. 渲染
npx remotion render RecorderVideo out/final.mp4

設定檔

整個影片的結構在 src/config.ts 中定義:

// src/config.ts
import { RecorderConfig } from "./types";
 
export const config: RecorderConfig = {
  // 錄製影片來源
  videoSrc: "my-recording.mp4",
 
  // 影片基本設定
  fps: 30,
  width: 1920,
  height: 1080,
 
  // 品牌設定
  brand: {
    name: "我的頻道",
    logo: "logo.png",
    primaryColor: "#6366f1",
    backgroundColor: "#0f0f1a",
  },
 
  // 章節定義
  chapters: [
    { startMs: 0, title: "簡介" },
    { startMs: 15000, title: "安裝與設定" },
    { startMs: 45000, title: "核心概念" },
    { startMs: 90000, title: "實作範例" },
    { startMs: 150000, title: "總結" },
  ],
 
  // 程式碼高亮區段
  codeHighlights: [
    {
      startMs: 47000,
      endMs: 62000,
      code: `npm install remotion\nnpx create-video@latest`,
      language: "bash",
      title: "安裝指令",
    },
    {
      startMs: 95000,
      endMs: 120000,
      code: `export const MyComp: React.FC = () => {
  const frame = useCurrentFrame();
  return <div style={{ opacity: frame / 30 }}>Hello</div>;
};`,
      language: "tsx",
      title: "基本元件",
    },
  ],
};

核心 Composition 結構

// remotion/RecorderVideo.tsx
import { AbsoluteFill, OffthreadVideo, staticFile, useCurrentFrame } from "remotion";
import { config } from "../src/config";
import { Captions } from "./components/Captions";
import { ChapterTitle } from "./components/ChapterTitle";
import { CodeHighlight } from "./components/CodeHighlight";
import { BrandOverlay } from "./components/BrandOverlay";
import captions from "../src/captions.json";
 
export const RecorderVideo: React.FC = () => {
  const frame = useCurrentFrame();
  const currentMs = (frame / config.fps) * 1000;
 
  // 找出目前啟用的程式碼高亮
  const activeHighlight = config.codeHighlights.find(
    (h) => currentMs >= h.startMs && currentMs <= h.endMs
  );
 
  // 找出目前章節
  const currentChapter = [...config.chapters]
    .reverse()
    .find((c) => currentMs >= c.startMs);
 
  return (
    <AbsoluteFill style={{ backgroundColor: config.brand.backgroundColor }}>
      {/* 背景:螢幕錄製影片 */}
      <OffthreadVideo
        src={staticFile(config.videoSrc)}
        style={{ width: "100%", height: "100%" }}
      />
 
      {/* 品牌 Logo 和頻道名稱 */}
      <BrandOverlay brand={config.brand} />
 
      {/* 章節標題 */}
      {currentChapter && (
        <ChapterTitle title={currentChapter.title} />
      )}
 
      {/* 程式碼高亮框(出現時疊加在影片上) */}
      {activeHighlight && (
        <CodeHighlight
          code={activeHighlight.code}
          language={activeHighlight.language}
          title={activeHighlight.title}
        />
      )}
 
      {/* 自動字幕 */}
      <Captions captions={captions} fps={config.fps} />
    </AbsoluteFill>
  );
};

程式碼高亮元件

// remotion/components/CodeHighlight.tsx
import { interpolate, useCurrentFrame } from "remotion";
import { Highlight, themes } from "prism-react-renderer";
 
interface CodeHighlightProps {
  code: string;
  language: string;
  title?: string;
}
 
export const CodeHighlight: React.FC<CodeHighlightProps> = ({
  code,
  language,
  title,
}) => {
  const frame = useCurrentFrame();
 
  const opacity = interpolate(frame, [0, 8], [0, 1], {
    extrapolateRight: "clamp",
  });
 
  const translateY = interpolate(frame, [0, 8], [20, 0], {
    extrapolateRight: "clamp",
  });
 
  return (
    <div
      style={{
        position: "absolute",
        bottom: 200,
        left: 80,
        right: 80,
        opacity,
        transform: `translateY(${translateY}px)`,
        borderRadius: 16,
        overflow: "hidden",
        boxShadow: "0 20px 60px rgba(0,0,0,0.6)",
      }}
    >
      {title && (
        <div
          style={{
            backgroundColor: "#1e1e2e",
            padding: "12px 24px",
            color: "#cdd6f4",
            fontSize: 28,
            fontFamily: "monospace",
          }}
        >
          {title}
        </div>
      )}
      <Highlight theme={themes.nightOwl} code={code} language={language}>
        {({ style, tokens, getLineProps, getTokenProps }) => (
          <pre
            style={{
              ...style,
              margin: 0,
              padding: "24px 32px",
              fontSize: 32,
              lineHeight: 1.6,
            }}
          >
            {tokens.map((line, i) => (
              <div key={i} {...getLineProps({ line })}>
                {line.map((token, key) => (
                  <span key={key} {...getTokenProps({ token })} />
                ))}
              </div>
            ))}
          </pre>
        )}
      </Highlight>
    </div>
  );
};

輸出格式選項

針對不同平台,可渲染不同規格:

# YouTube(16:9 Full HD)
npx remotion render RecorderVideo out/youtube.mp4 \
  --width=1920 --height=1080
 
# Twitter/X 短影音(1:1 方形)
npx remotion render RecorderVideoSquare out/twitter.mp4 \
  --width=1080 --height=1080
 
# Instagram Reels / TikTok(9:16 垂直)
npx remotion render RecorderVideoVertical out/reels.mp4 \
  --width=1080 --height=1920

延伸閱讀