在 Svelte 中使用 Remotion
學習如何將 Remotion Player 整合到 Svelte 應用程式中,包括建立 React 包裝器和雙向數據綁定
本指南說明如何將 Remotion 整合到 Svelte 專案中。
安裝必要套件
安裝 Remotion 和必要的依賴項:
npm i remotion @remotion/player @remotion/cli react react-dom
npm i --save-dev @types/react @types/react-dompnpm i remotion @remotion/player @remotion/cli react react-dom
pnpm i --dev @types/react @types/react-domyarn add remotion @remotion/player @remotion/cli react react-dom
yarn add --dev @types/react @types/react-dombun i remotion @remotion/player @remotion/cli react react-dom
bun --dev @types/react @types/react-dom注意:Bun 作為執行時大部分受到支援。在此閱讀更多。
建立 Remotion 資料夾
為了更好地分離關注點,建立一個資料夾來存放你的 Remotion 檔案:
src/remotion
將你的 Remotion 專案或入門範本(例如 HelloWorld)的內容複製到這個新資料夾中。
複製 remotion.config.ts
將 remotion.config.ts 檔案複製到 Svelte 專案的根目錄,放置在與 package.json 相同的層級。
import { Config } from '@remotion/cli/config';
Config.setVideoImageFormat('jpeg');
Config.setOverwriteOutput(true);為 JSX 配置 TypeScript
要在 Svelte 中啟用 JSX 支援,請在 tsconfig.app.json 的 compilerOptions 下設定 "jsx": "react":
{
"compilerOptions": {
"jsx": "react"
}
}為 Svelte 建立 React 包裝器組件
要在 Svelte 中嵌入 Remotion 組件,需要建立一個包裝器組件:
在你的 remotion 資料夾中,建立一個名為 PlayerViewWrapper.svelte 的檔案:
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
import React from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { type PlayerSchema, PlayerView } from './PlayerView';
import { type PlayerRef } from '@remotion/player';
let { data, player = $bindable(), onPaused } = $props<{
data: PlayerSchema;
player?: PlayerRef | null;
onPaused?: () => void;
}>();
let containerRef: HTMLDivElement;
let root: Root;
// 當 data 改變時重新渲染播放器
$effect(() => {
// 需要在 $effect 中存取屬性,因為 Svelte 不會自動偵測深層變更
console.log(data.titleText);
render();
});
function render() {
if (!containerRef || !root) return;
root.render(
React.createElement(PlayerView, {
ref: (ref: any) => {
player = ref?.playerRef;
},
data,
onPaused,
})
);
}
onMount(() => {
root = createRoot(containerRef);
render();
});
onDestroy(() => {
root?.unmount();
});
</script>
<div bind:this={containerRef}></div>此包裝器組件將作為 Svelte 和 Remotion React 組件之間的橋樑。
為 Remotion Player 建立包裝器
- 在你的
remotion資料夾中,建立一個名為PlayerView.tsx的檔案。 - 確保每個
.tsx檔案在頂部明確匯入 React:
此檔案使用 createRef 獲取播放器的參考:
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';
import { Player, type PlayerRef } from '@remotion/player';
import { HelloWorld } from './HelloWorld';
export interface PlayerSchema {
titleText: string;
titleColor: string;
logoColor1: string;
logoColor2: string;
}
export const PlayerView = forwardRef<
{ playerRef: PlayerRef | null },
{
data: PlayerSchema;
onPaused?: () => void;
}
>((props, ref) => {
const playerRef: React.RefObject<PlayerRef> = React.createRef();
useEffect(() => {
if (playerRef.current) {
// 當播放器暫停時新增回調
playerRef.current.addEventListener('pause', () => {
props.onPaused?.();
});
}
}, []);
useImperativeHandle(ref, () => ({
get playerRef() {
return playerRef.current;
},
}));
return (
<Player
ref={playerRef}
component={HelloWorld}
inputProps={props.data}
durationInFrames={150}
fps={30}
compositionWidth={1920}
compositionHeight={1080}
style={{ width: '100%' }}
/>
);
});在 Svelte 中使用組件
在你的 Svelte 應用程式中使用包裝器組件:
<script lang="ts">
import PlayerViewWrapper from '../remotion/PlayerViewWrapper.svelte';
import { type PlayerRef } from '@remotion/player';
import type { PlayerSchema } from '../remotion/PlayerView';
let player: PlayerRef | null = $state(null);
let videoData: PlayerSchema = $state({
titleText: '你好,Svelte + Remotion!',
titleColor: '#ffffff',
logoColor1: '#91EAE4',
logoColor2: '#86A8E7',
});
function handlePaused() {
console.log('播放器已暫停');
}
function updateTitle() {
videoData.titleText = '更新後的標題';
}
function playVideo() {
player?.play();
}
function pauseVideo() {
player?.pause();
}
</script>
<main>
<h1>我的影片應用</h1>
<PlayerViewWrapper
data={videoData}
bind:player
onPaused={handlePaused}
/>
<div class="controls">
<button onclick={playVideo}>播放</button>
<button onclick={pauseVideo}>暫停</button>
<button onclick={updateTitle}>更新標題</button>
</div>
</main>
<style>
.controls {
margin-top: 16px;
display: flex;
gap: 8px;
}
button {
padding: 8px 16px;
border-radius: 4px;
border: none;
background: #4CAF50;
color: white;
cursor: pointer;
}
</style>使用 SvelteKit 的伺服器端渲染
如果你使用 SvelteKit,需要防止在伺服器端渲染時載入 Remotion 組件:
<script lang="ts">
import { browser } from '$app/environment';
import { onMount } from 'svelte';
let PlayerViewWrapper: any = $state(null);
onMount(async () => {
// 只在瀏覽器端載入 Remotion 組件
const module = await import('../remotion/PlayerViewWrapper.svelte');
PlayerViewWrapper = module.default;
});
</script>
{#if browser && PlayerViewWrapper}
<svelte:component
this={PlayerViewWrapper}
data={{
titleText: '你好,Remotion!',
titleColor: '#ffffff',
logoColor1: '#91EAE4',
logoColor2: '#86A8E7',
}}
/>
{:else}
<div>載入中...</div>
{/if}啟動 Remotion Studio
在你的 Svelte 專案根目錄中執行:
npx remotion studio