Remotion LabRemotion Lab
客戶端渲染取消客戶端渲染

取消客戶端渲染

使用 AbortController API 取消進行中的客戶端渲染,以及偵測渲染是否被取消的方法。

取消客戶端渲染

警告 — 實驗性功能:此功能隨時可能出現錯誤和重大變更。請在 GitHub 上追蹤進度,並在 Discord 的 #web-renderer 頻道中參與討論。

renderMediaOnWeb()renderStillOnWeb() 均透過 AbortSignal API 支援取消操作。

使用 AbortController

建立一個 AbortController 並將其 signal 傳遞給渲染函式:

在逾時後取消渲染
import {renderMediaOnWeb} from '@remotion/web-renderer';
 
const Component: React.FC = () => null;
 
const composition = {
  component: Component,
  durationInFrames: 100,
  fps: 30,
  width: 100,
  height: 100,
  id: 'my-composition',
};
 
const abortController = new AbortController();
 
// 10 秒後取消
setTimeout(() => abortController.abort(), 10000);
 
const {getBlob} = await renderMediaOnWeb({
  signal: abortController.signal,
  composition,
});

偵測渲染是否被取消

當渲染被取消時,會拋出一個錯誤。若要區分使用者主動取消和實際錯誤,請檢查 signal 是否已中止:

在 catch 區塊中處理取消
import {renderMediaOnWeb} from '@remotion/web-renderer';
 
const abortController = new AbortController();
 
try {
  const {getBlob} = await renderMediaOnWeb({
    signal: abortController.signal,
    composition,
  });
  // 取得 blob 並進行後續處理
  const blob = await getBlob();
  const url = URL.createObjectURL(blob);
  // 下載或顯示影片
} catch (error) {
  if (abortController.signal.aborted) {
    // 渲染被使用者取消,優雅地處理
    console.log('渲染已取消');
  } else {
    // 處理實際錯誤
    throw error;
  }
}

在 React 元件中使用

在 React 元件中,通常需要在元件卸載時取消渲染,以避免記憶體洩漏:

在元件卸載時取消渲染
import React, {useEffect, useRef, useState} from 'react';
import {renderMediaOnWeb} from '@remotion/web-renderer';
 
const RenderButton: React.FC = () => {
  const [isRendering, setIsRendering] = useState(false);
  const abortControllerRef = useRef<AbortController | null>(null);
 
  const startRender = async () => {
    // 如果有進行中的渲染,先取消它
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }
 
    const controller = new AbortController();
    abortControllerRef.current = controller;
    setIsRendering(true);
 
    try {
      const {getBlob} = await renderMediaOnWeb({
        signal: controller.signal,
        composition: {
          component: MyVideoComponent,
          durationInFrames: 300,
          fps: 30,
          width: 1920,
          height: 1080,
          id: 'my-video',
        },
        inputProps: {},
      });
      const blob = await getBlob();
      // 處理 blob...
    } catch (error) {
      if (!controller.signal.aborted) {
        console.error('渲染失敗:', error);
      }
    } finally {
      setIsRendering(false);
    }
  };
 
  const cancelRender = () => {
    abortControllerRef.current?.abort();
    setIsRendering(false);
  };
 
  // 元件卸載時取消進行中的渲染
  useEffect(() => {
    return () => {
      abortControllerRef.current?.abort();
    };
  }, []);
 
  return (
    <div>
      <button onClick={startRender} disabled={isRendering}>
        {isRendering ? '渲染中...' : '開始渲染'}
      </button>
      {isRendering && (
        <button onClick={cancelRender}>取消渲染</button>
      )}
    </div>
  );
};

相關資源