透過以特定方式撰寫 Remotion 標記,Remotion Studio 就能夠辨識程式碼的結構並使其具備互動性:
- 允許透過點擊選取項目
- 允許拖放、調整大小和旋轉
- 編輯 CSS 樣式
- 讓關鍵影格和 easing 值可編輯
如果標記過於複雜,以至於 Studio 無法使其互動,則這些值會變成灰色。
偏好使用具備獨立時間軸的互動元件
對於填滿其父容器的元件,請使用 Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'})。這個包裝器提供了它們的視覺根節點和時間軸項目,並預設裁剪溢出的內容,因此內部元件可以回傳一個片段 (fragment)。將時序、預掛載 (premounting)、尺寸和每個實例的樣式放在匯出的元件上。
將屬於同一視覺元素的內容保留在同一個元件中,以便它們可以一起移動和修剪。內部佈局容器仍然可以提供對齊、在較小區域內進行裁剪或一個動畫群組;一個僅填滿場景的容器通常是不必要的。卡片、文字框和具有自身邊界的 SVG 可以保留預設的 layout: 'none',並將 style 轉發給它們的視覺根節點。
import {Video} from '@remotion/media';
import type React from 'react';
import {Interactive, useVideoConfig, type InteractivitySchema} from 'remotion';
type SceneProps = {
readonly title: string;
};
const SceneInner: React.FC<SceneProps> = ({title}) => {
const {fps} = useVideoConfig();
return (
<>
<Video
name="背景素材"
src="https://remotion.media/video.mp4"
premountFor={fps}
objectFit="cover"
style={{width: '100%', height: '100%'}}
/>
<Interactive.Div
name="場景標題"
style={{
position: 'absolute',
left: 80,
bottom: 80,
color: 'white',
fontFamily: 'Helvetica, Arial, sans-serif',
fontSize: 80,
}}
>
{title}
</Interactive.Div>
</>
);
};
const sceneSchema = {
title: {type: 'string', default: '', description: '標題'},
} as const satisfies InteractivitySchema;
export const Scene = Interactive.withSchema({
Component: SceneInner,
componentName: 'Scene',
schema: sceneSchema,
wrapInSequence: true,
layout: 'absolute-fill',
});
選擇 Schema 欄位
Schema 中的每個鍵都是一個 prop,Studio 可以在呼叫端讀取和編輯。
不在 schema 中的 props 仍然可以正常工作,但在 Studio 中無法編輯。
鍵可以使用點記號,例如 style.color。
| 類型 | 用途 | 可設定關鍵影格 |
|---|---|---|
string | 字串,例如標題、提示或程式碼片段 | 否 |
number | 數字,可選用 min、max、step、integer | 是 |
boolean | 開關 | 是 (保持) |
color | CSS 顏色字串 | 是 |
enum | 在 variants 之間進行選擇,每個都有其自己的巢狀 schema | 可選 (保持) |
array | 數字、顏色、列舉等的列表 | 否 |
asset | 媒體來源,可選用 assetType | 否 |
font-family | CSS 字型家族 | 否 |
font-weight | 字重 | 是 |
translate, scale, rotation-css, rotation-degrees, transform-origin | 變換 | 是 |
uv-coordinate | 元素上一個標準化的 [x, y] 點 | 是 |
svg-path | SVG 路徑資料 | 是 |
remotion-captions | Caption[] 資料 | 否 |
hidden | 一個不顯示在控制項中的值 | — |
每個欄位都需要一個 default,並且應該有一個 description,Studio 會將其顯示為標籤。
設定 keyframable: false 以顯示靜態控制項。
暴露內容和每個實例的外觀,例如文字和強調色。
不要為 style.translate、style.scale、style.rotate、style.transformOrigin 和 style.opacity 添加欄位:wrapInSequence: true 已經添加了它們。
要使根節點的 CSS 屬性在每個實例中可編輯,可以展開內建的 schema 片段:
const cardSchema = {
...Interactive.textSchema, // style.color, style.fontSize, style.fontWeight, ...
...Interactive.backgroundSchema, // style.backgroundColor
...Interactive.borderRadiusSchema, // style.borderRadius, ...
} as const satisfies InteractivitySchema;
也可用:Interactive.borderSchema,以及用於接受字幕的元件的 Interactive.captionsSchema。
對於接受 React.ReactNode 子元素的元件,請添加 ...Interactive.childrenSchema。
它為子元素內容提供控制項,並可能在未來演進以支援更豐富的內容。
目前,它使用一個標籤為「文字」且預設值為 '' 的字串欄位。
如果一個元件僅接受字串類型的子元素,請改用 type: 'string' 的顯式 children 欄位。
Interactive.textSchema 控制排版樣式;childrenSchema 控制內容。
子元素片段不包含在 baseSchema 中,也不會自動添加到自訂元件。
巢狀標記和計算後的子元素目前是唯讀的。
text-content 是 string 欄位類型的一個已棄用的別名。
有關所有選項,請參閱 InteractivitySchema。
將可重用元件註冊為已連結的合成 (Connected Composition)
將重要的場景和可重用元件(具有自己的圖層或動畫)註冊為獨立的合成 (Composition)。Studio 將這些稱為已連結的合成:它們可以在自己的時間軸中開啟,同時與父影片共享同一個元件實作。
在元件內部定義所需的樣式並載入字型,使其在沒有父元件的情況下也能正常運作。請參閱父元件獨立性。
註冊父元件所渲染的同一個匯出元件參考。對於上面的範例,請使用 component={Scene},而不是 SceneInner 或內聯包裝器。使用 wrapInSequence: true 時,不需要額外的 <Sequence> 來建立連線。
import {Composition} from 'remotion';
import {Scene} from './Scene';
export const RemotionRoot = () => (
<Composition
id="Scene"
component={Scene}
width={1280}
height={720}
fps={30}
durationInFrames={30}
defaultProps={{
title: '一個新的視角',
}}
/>
);
選擇一個唯一的 ID、代表性的內聯預設值以及適合預覽元件的尺寸、fps 和持續時間。註冊預設值不會自動從父實例複製 props。有關註冊和提取的詳細資訊,請參閱已連結的合成。
直接將時序放在元件上
避免在一個已接受時序 props 的元件周圍使用單獨的 <Sequence>。使用 wrapInSequence: true 建立的內建互動元件和自訂元件可以直接接收 name、from、durationInFrames、trimBefore、playbackRate 和 premountFor。
<Scene
name="開場場景"
from={30}
durationInFrames={90}
premountFor={fps}
style={{opacity: 0.9}}
title="一個新的視角"
/>
移除冗餘的 sequence 時,將其時序和名稱移動到子元件。保留任何現有的子元件時序;不要覆蓋或雙重套用偏移量。元件內部的 useCurrentFrame() 呼叫讀取其局部影格,而呼叫端的樣式表達式則使用呼叫者的影格。在移動動畫樣式時,請保留此區別。
在以下情況保留 <Sequence>:當它為多個兄弟元件提供共享時序、包裝了一個不處理時序的元件,或提供了子元件無法直接接受的尺寸。受管理的根節點也可以接受 width 和 height 以用於 useVideoConfig()。保留 <TransitionSeries.Sequence> 用於連續佈局,無論是否有轉場效果;直接的 from props 不會取代這些行為。
為每個可獨立編輯的項目建立其專屬的 JSX 節點
Studio 會編輯建立項目的 JSX 原始節點。如果多個執行時期項目來自同一個 JSX 節點,它們會共享同一個原始碼編輯目標。
對於每個合成註冊、剪輯、場景、圖層或序列(它們應該可以獨立編輯),請撰寫一個單獨的 JSX 節點,並將其可編輯的 props 保留在該節點上。這適用於 <Composition>、<Still>、內建媒體元件、<Sequence>、<TransitionSeries.Sequence> 和自訂元件。
例如,像這樣撰寫一個可編輯的時間軸:
import { TransitionSeries } from "@remotion/transitions";
<TransitionSeries>
<TransitionSeries.Sequence name="介紹" durationInFrames={90}>
<Introduction />
</TransitionSeries.Sequence>
<TransitionSeries.Sequence name="示範" durationInFrames={150}>
<Demo />
</TransitionSeries.Sequence>
</TransitionSeries>
一個 .map() 或其他程式化迴圈會從一個 JSX 原始節點建立多個執行時期項目。這適用於需要作為一個模板進行控制的、重複的輸出,例如視覺化長條圖或粒子。但當實例在 Studio 中需要獨立的 ID 或名稱、props、元資料、時序、排序、刪除或複製時,這種做法就不合適。
使用 Interactive 讓 HTML 元素具有互動性
每個 HTML 和 SVG 元素(<Img> 除外,它已經具有互動性)例如 <div>,都可以使用 Interactive 來使其具有互動性:
<Interactive.Div
name="問候卡片"
style={{fontSize: 80, padding: 24}}
>
你好
</Interactive.Div>
這允許在 Studio 中設定樣式和關鍵影格。要有節制地使用,如果一個元件有太多元素,時間軸可能會變得雜亂無章。
偏好內聯文字
如果文字是固定的且只使用一次,請直接將其寫在互動元素內部,而不是將其提取為一個常數。
// 👍 固定的文字保持可編輯
<Interactive.Div name="標題">
Remotion 最佳實踐
</Interactive.Div>
僅當文字是動態的或被重複使用時,才使用 prop 或變數。
為互動元素提供描述性的名稱
為元素添加 name prop,使其易於識別。
避免使用計算出來的名稱,請寫死它們。
<>
<Interactive.Div name="英雄標題" style={{fontSize: 80}}>
發佈日
</Interactive.Div>
<Img name="頭像" src="https://remotion.media/image.jpeg" />
<Video name="背景" src="https://remotion.media/video.mp4" />
<Interactive.Div name="標題">
發佈日
</Interactive.Div>
</>
將所有 CSS 樣式保持內聯
最好的方法是直接將一個純粹的物件傳遞給 style - 不要引用常數、不要展開物件、不要進行數學運算。
<Interactive.Div
style={{
fontSize: 80,
color: 'red',
}}
>
哈囉,世界!
</Interactive.Div>
const baseStyle = useMemo(() => {
return {
fontSize: 12 // ❌ 不支援非內聯樣式
}
}, []);
<Interactive.Div
style={{
...baseStyle, // ❌ 不支援展開
color: RED, // ❌ 不支援引用常數
scale: frame * 10 // ❌ 不支援數學運算
}}
>
哈囉,世界!
</Interactive.Div>
使用 interpolate() 來製作動畫
將動畫撰寫為作用於改變的屬性上的內聯 interpolate() 呼叫。
輸出範圍、easing、外推法 (extrapolation) 和 output 屬性應使用寫死的值。
輸入範圍還可以使用從 useVideoConfig() 直接解構出來的 durationInFrames、fps、width 和 height。支援使用裸識別符號,例如 durationInFrames、與數字相乘,例如 2 * fps 或 fps * 2,以及減去一個數字,例如 durationInFrames - 1。
const {fps, durationInFrames} = useVideoConfig();
// 👍 內聯值可以標準化和設定關鍵影格
<Interactive.Div
name="產品卡片"
style={{
color: 'white',
fontSize: 80,
scale: interpolate(frame, [0, fps], [0, 1], {
easing: Easing.spring({damping: 200}),
output: 'perceptual-scale',
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
}),
rotate: interpolate(frame, [0, 1 * fps], ['0deg', '20deg'], {
easing: Easing.spring({damping: 200}),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
}),
translate: interpolate(
frame,
[durationInFrames - 30, durationInFrames],
['0px 0px', '0px 120px'],
{
easing: Easing.spring({damping: 200}),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
}
),
}}
>
哈囉,世界!
</Interactive.Div>
