Remotion LabRemotion Lab
參數化影片資料擷取

資料擷取

在 Remotion 影片中使用外部 API 資料與動態內容。

兩種資料擷取時機

在 Remotion 中取得外部資料有兩種方式,對應兩個不同的時機:

方式時機適用場景
calculateMetadata渲染前(決定影片規格時)影片長度、解析度需要根據資料決定
delayRender / continueRender渲染中(元件 mount 時)載入圖片、字型、API 資料

方式一:calculateMetadata

在渲染開始前擷取資料,適合需要根據資料決定影片規格的場景:

import { CalculateMetadataFunction } from "remotion";
 
interface VideoProps {
  apiUrl: string;
  data?: { items: string[]; title: string };
}
 
export const calculateMetadata: CalculateMetadataFunction<
  VideoProps
> = async ({ props }) => {
  // 在渲染前呼叫 API
  const response = await fetch(props.apiUrl);
  const data = await response.json();
 
  return {
    // 根據資料量決定影片長度
    durationInFrames: data.items.length * 90,
    // 把資料傳入 Props
    props: {
      ...props,
      data,
    },
  };
};

calculateMetadata 的回傳值可以覆蓋 durationInFramesfpswidthheight,也可以修改傳給元件的 props

在 Composition 中使用

<Composition
  id="DataVideo"
  component={DataVideo}
  calculateMetadata={calculateMetadata}
  defaultProps={{
    apiUrl: "https://api.example.com/data",
  }}
/>

方式二:delayRender / continueRender

在元件內部擷取資料。Remotion 預設會立刻開始截圖每一幀,但有時你需要等資料載入完成。delayRender() 告訴 Remotion「先等一下」,continueRender() 告訴它「好了,可以開始了」:

import { useCallback, useEffect, useState } from "react";
import { delayRender, continueRender, AbsoluteFill } from "remotion";
 
export const DataDrivenScene: React.FC = () => {
  const [data, setData] = useState<string[] | null>(null);
  const [handle] = useState(() => delayRender("正在載入資料..."));
 
  const fetchData = useCallback(async () => {
    try {
      const response = await fetch("https://api.example.com/items");
      const json = await response.json();
      setData(json.items);
      continueRender(handle);
    } catch (err) {
      // 錯誤處理也必須呼叫 continueRender,否則渲染會永遠卡住
      console.error(err);
      continueRender(handle);
    }
  }, [handle]);
 
  useEffect(() => {
    fetchData();
  }, [fetchData]);
 
  if (!data) {
    return null;
  }
 
  return (
    <AbsoluteFill style={{ backgroundColor: "#0f0f1a" }}>
      {data.map((item, i) => (
        <div key={i} style={{ color: "white", fontSize: 36 }}>
          {item}
        </div>
      ))}
    </AbsoluteFill>
  );
};

重要規則

  1. 一定要呼叫 continueRender() — 不管成功或失敗,都必須呼叫,否則渲染會無限等待直到超時。
  2. delayRender() 必須在元件第一次 render 時呼叫 — 不能放在 useEffect 裡,要放在 useState 的初始值中。
  3. 有超時限制 — 預設 30 秒。如果資料載入超過這個時間,渲染會失敗。

載入圖片

載入遠端圖片也是一種資料擷取。用 <Img> 元件(注意大寫 I)會自動處理 delayRender:

import { Img } from "remotion";
 
export const ImageScene: React.FC<{ imageUrl: string }> = ({ imageUrl }) => {
  return (
    <AbsoluteFill>
      <Img src={imageUrl} style={{ width: "100%" }} />
    </AbsoluteFill>
  );
};

<Img> 內部會自動呼叫 delayRender(),等圖片載入完成後才讓 Remotion 繼續渲染。如果你用原生 <img> 標籤,圖片可能還沒載入就被截圖了。

載入字型

Google Fonts 或自訂字型也需要等待載入:

import { useState } from "react";
import { delayRender, continueRender } from "remotion";
 
const waitForFont = delayRender("正在載入字型...");
 
const font = new FontFace(
  "MyCustomFont",
  "url(https://example.com/font.woff2)"
);
 
font
  .load()
  .then(() => {
    document.fonts.add(font);
    continueRender(waitForFont);
  })
  .catch((err) => {
    console.error("字型載入失敗", err);
    continueRender(waitForFont);
  });

選擇哪種方式?

需要根據資料決定影片長度或解析度?
  → 用 calculateMetadata

只是在元件中顯示外部資料?
  → 用 delayRender / continueRender

載入圖片?
  → 用 Remotion 的 <Img> 元件(自動處理)

載入字型?
  → 用 delayRender + FontFace API

小結

  • calculateMetadata — 渲染前取得資料,可以動態決定影片規格和 Props
  • delayRender / continueRender — 渲染中暫停等待資料,記得一定要呼叫 continueRender()
  • <Img> — Remotion 的圖片元件,自動等待載入完成
  • 不管用哪種方式,都要處理錯誤情況,避免渲染無限卡住