Remotion LabRemotion Lab
建構應用在 Vue 中使用 Remotion

在 Vue 中使用 Remotion

學習如何將 Remotion Player 整合到 Vue.js 應用程式中,包括 Vite 設定、React 包裝器和響應式數據更新

本指南說明如何將 Remotion 整合到 Vue.js 專案中。

安裝必要套件

安裝 Remotion 和必要的依賴項:

使用 npm
npm i remotion @remotion/player @remotion/cli react react-dom
npm i --save-dev @types/react @types/react-dom @vitejs/plugin-react
使用 pnpm
pnpm i remotion @remotion/player @remotion/cli react react-dom
pnpm i --dev @types/react @types/react-dom @vitejs/plugin-react
使用 Yarn
yarn add remotion @remotion/player @remotion/cli react react-dom
yarn add --dev @types/react @types/react-dom @vitejs/plugin-react
使用 Bun
bun i remotion @remotion/player @remotion/cli react react-dom
bun --dev @types/react @types/react-dom @vitejs/plugin-react

注意:Bun 作為執行時大部分受到支援。在此閱讀更多

建立 Remotion 資料夾

為了更好地分離關注點,建立一個資料夾來存放你的 Remotion 檔案:

src/remotion

將你的 Remotion 專案或入門範本(例如 HelloWorld)的內容複製到這個新資料夾中。

複製 remotion.config.ts

remotion.config.ts 檔案複製到 Vue.js 專案的根目錄,放置在與 package.json 相同的層級。

remotion.config.ts
import { Config } from '@remotion/cli/config';
 
Config.setVideoImageFormat('jpeg');
Config.setOverwriteOutput(true);

為 JSX 配置 TypeScript

要在 Vue.js 中啟用 JSX 支援,請在 tsconfig.app.jsoncompilerOptions 下設定 "jsx": "react"

tsconfig.json
{
  "compilerOptions": {
    "jsx": "react",
    "jsxImportSource": ""
  }
}

注意:建議設定 "jsxImportSource": "" 以防止建構問題。

為 React 配置 Vite

要在 Vite 中啟用 React 支援,請在 vite.config.ts 中將 react 加入插件中:

vite.config.ts
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import react from '@vitejs/plugin-react';
 
export default defineConfig({
  plugins: [
    vue(),
    react({
      include: /\.(jsx|tsx)$/,
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
});

為 Vue.js 建立 React 包裝器組件

要在 Vue.js 中嵌入 Remotion 組件,需要建立一個包裝器組件

在你的 remotion 資料夾中,建立一個名為 PlayerViewWrapper.vue 的檔案:

PlayerViewWrapper.vue
<template>
  <div ref="reactRoot"></div>
</template>
 
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { type PlayerSchema, PlayerView } from '@/remotion/PlayerView';
import { createRoot, type Root } from 'react-dom/client';
import * as React from 'react';
 
interface PlayerData {
  data: PlayerSchema;
}
 
const playerRef = ref();
const reactRoot = ref<HTMLElement>();
let root: Root;
const emit = defineEmits(['paused']);
 
const props = defineProps<PlayerData>();
 
onMounted(() => {
  root = createRoot(reactRoot.value!);
  render();
});
 
function render() {
  root.render(
    React.createElement(PlayerView, {
      data: props.data,
      playerRefInstance: playerRef,
      onPaused: () => emit('paused'),
    })
  );
}
 
// 監聽 playerRef 變化(播放器載入後自動播放)
watch(
  () => playerRef,
  (newData) => {
    playerRef.value.play();
  },
  { deep: true }
);
 
// 監聽 data 變化(當 props 改變時重新渲染)
watch(
  () => props.data,
  (newData) => {
    render();
  },
  { deep: true }
);
 
onBeforeUnmount(() => {
  if (root) {
    root.unmount();
  }
});
</script>

此包裝器組件將作為 Vue.js 和 Remotion React 組件之間的橋樑。

為 Remotion Player 建立包裝器

  1. 在你的 remotion 資料夾中,建立一個名為 PlayerView.tsx 的檔案。
  2. 確保每個 .tsx 檔案在頂部明確匯入 React:
PlayerView.tsx
import React, { useEffect } from 'react';
import { Player, type PlayerRef } from '@remotion/player';
import { HelloWorld } from '@/remotion/HelloWorld';
import type { Ref } from 'vue';
 
export interface PlayerSchema {
  titleText: string;
  titleColor: string;
  logoColor1: string;
  logoColor2: string;
}
 
export const PlayerView = ({
  data,
  playerRefInstance,
  onPaused,
}: {
  data: PlayerSchema;
  playerRefInstance?: Ref<PlayerRef | undefined>;
  onPaused?: () => void;
}) => {
  const playerRef = React.createRef<PlayerRef>();
 
  useEffect(() => {
    if (playerRef.current) {
      // 將 React ref 同步到 Vue ref
      if (playerRefInstance) {
        playerRefInstance.value = playerRef.current;
      }
 
      // 當播放器暫停時新增回調
      playerRef.current.addEventListener('pause', () => {
        onPaused?.();
      });
    }
  }, []);
 
  return (
    <Player
      ref={playerRef}
      component={HelloWorld}
      inputProps={data}
      durationInFrames={150}
      fps={30}
      compositionWidth={1920}
      compositionHeight={1080}
      style={{ width: '100%' }}
    />
  );
};

在 Vue 中使用組件

在你的 Vue 應用程式中使用包裝器組件:

App.vue
<template>
  <div>
    <h1>我的影片應用</h1>
 
    <PlayerViewWrapper
      :data="videoData"
      @paused="handlePaused"
    />
 
    <div class="controls">
      <button @click="updateTitle">更新標題</button>
      <button @click="updateColor">更改顏色</button>
    </div>
  </div>
</template>
 
<script setup lang="ts">
import { reactive } from 'vue';
import PlayerViewWrapper from '@/remotion/PlayerViewWrapper.vue';
import type { PlayerSchema } from '@/remotion/PlayerView';
 
const videoData = reactive<PlayerSchema>({
  titleText: '你好,Vue + Remotion!',
  titleColor: '#ffffff',
  logoColor1: '#91EAE4',
  logoColor2: '#86A8E7',
});
 
function handlePaused() {
  console.log('播放器已暫停');
}
 
function updateTitle() {
  videoData.titleText = '更新後的標題 ' + new Date().toLocaleTimeString();
}
 
function updateColor() {
  videoData.titleColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
}
</script>
 
<style scoped>
.controls {
  margin-top: 16px;
  display: flex;
  gap: 8px;
}
 
button {
  padding: 8px 16px;
  border-radius: 4px;
  background: #42b883;
  color: white;
  border: none;
  cursor: pointer;
}
 
button:hover {
  background: #2c9e6e;
}
</style>

使用 Nuxt.js

如果你使用 Nuxt.js,需要配置自動匯入並確保 Remotion 只在客戶端執行:

nuxt.config.ts
export default defineNuxtConfig({
  vite: {
    plugins: [
      // 需要手動引入 @vitejs/plugin-react
    ],
  },
  // 確保 Remotion 模組只在客戶端載入
  ssr: false,
});
pages/index.vue(Nuxt)
<template>
  <div>
    <ClientOnly>
      <PlayerViewWrapper
        :data="videoData"
        @paused="handlePaused"
      />
      <template #fallback>
        <div>載入播放器中...</div>
      </template>
    </ClientOnly>
  </div>
</template>

啟動 Remotion Studio

在你的 Vue 專案根目錄中執行:

npx remotion studio

渲染影片

你也可以在本地渲染影片:

npx remotion render src/remotion/index.ts MyComposition output.mp4

另請參閱