<AbsoluteFill> 元件
使用 <AbsoluteFill> 元件快速建立填滿整個畫面的絕對定位容器,並透過圖層疊加建構複雜的視覺效果。
什麼是 <AbsoluteFill>?
<AbsoluteFill> 是一個輔助元件,它本質上是一個帶有以下樣式的絕對定位 <div>:
// AbsoluteFill 的內建樣式
const style: React.CSSProperties = {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
};這個元件非常適合將內容疊加在彼此上方。
基本使用
全螢幕影片背景範例
import { AbsoluteFill, OffthreadVideo } from "remotion";
const MyComp = () => {
return (
<AbsoluteFill>
{/* 底層:影片背景 */}
<AbsoluteFill>
<OffthreadVideo src="https://example.com/video.mp4" />
</AbsoluteFill>
{/* 上層:文字覆蓋 */}
<AbsoluteFill>
<h1>這段文字顯示在影片上方!</h1>
</AbsoluteFill>
</AbsoluteFill>
);
};後面渲染的元素會疊在前面的上方——這是 HTML 的標準行為(由 DOM 順序決定堆疊順序)。
圖層堆疊原理
由於 <AbsoluteFill> 使用 position: absolute,多個 <AbsoluteFill> 放在一起時,它們會像 Photoshop 的圖層一樣堆疊:
import { AbsoluteFill } from "remotion";
export const LayeredScene: React.FC = () => {
return (
<AbsoluteFill>
{/* 第 1 層(最底層):背景色 */}
<AbsoluteFill style={{ backgroundColor: "#0f0f1a" }} />
{/* 第 2 層:裝飾圓形 */}
<AbsoluteFill style={{ opacity: 0.4 }}>
<div
style={{
width: 600,
height: 600,
borderRadius: "50%",
background: "radial-gradient(#6366f1, transparent)",
position: "absolute",
top: -150,
right: -150,
}}
/>
</AbsoluteFill>
{/* 第 3 層(最上層):主要內容 */}
<AbsoluteFill
style={{ justifyContent: "center", alignItems: "center" }}
>
<h1 style={{ color: "white", fontSize: 72 }}>主要標題</h1>
</AbsoluteFill>
</AbsoluteFill>
);
};加入 React Ref
從 v3.2.13 起,你可以為 <AbsoluteFill> 加入 React ref。若使用 TypeScript,需要以 HTMLDivElement 來指定型別:
const MyComp = () => {
const ref = useRef<HTMLDivElement>(null);
return (
<AbsoluteFill ref={ref}>
{content}
</AbsoluteFill>
);
};TailwindCSS 類別偵測 v4.0.249
<AbsoluteFill> 的內建 style 物件比 className 有更高的優先級。為了讓你的 Tailwind 類別如預期生效,Remotion 從 v4.0.249 起會偵測衝突的 Tailwind 類別,並在偵測到時停用相對應的 inline 樣式。
例如,若你想要橫向排列:
<AbsoluteFill className="flex flex-row" />Remotion 會偵測到 flex-row 覆蓋了 flexDirection: "column",並自動停用 inline 的 flexDirection 樣式,讓 Tailwind 類別生效。
詳細的偵測邏輯請查看元件的原始碼。
進階應用範例
搭配動畫的圖層
每個圖層可以獨立進行動畫,並互不干擾:
import { AbsoluteFill, useCurrentFrame, interpolate } from "remotion";
export const AnimatedLayers: React.FC = () => {
const frame = useCurrentFrame();
// 背景淡入
const bgOpacity = interpolate(frame, [0, 30], [0, 1], {
extrapolateRight: "clamp",
});
// 文字從下方滑入
const textY = interpolate(frame, [15, 45], [50, 0], {
extrapolateRight: "clamp",
});
// 文字淡入
const textOpacity = interpolate(frame, [15, 45], [0, 1], {
extrapolateRight: "clamp",
});
return (
<AbsoluteFill>
{/* 背景層 */}
<AbsoluteFill
style={{
backgroundColor: "#0f0f1a",
opacity: bgOpacity,
}}
/>
{/* 文字層 */}
<AbsoluteFill
style={{
justifyContent: "center",
alignItems: "center",
transform: `translateY(${textY}px)`,
opacity: textOpacity,
}}
>
<h1 style={{ color: "white", fontSize: 64 }}>動畫標題</h1>
</AbsoluteFill>
</AbsoluteFill>
);
};搭配 <Sequence> 控制圖層時序
結合 <Sequence> 讓各圖層在不同時間出現:
import { AbsoluteFill, Sequence } from "remotion";
export const TimedLayers: React.FC = () => {
return (
<AbsoluteFill style={{ backgroundColor: "#0f0f1a" }}>
{/* 背景立即出現 */}
<Sequence from={0}>
<Background />
</Sequence>
{/* 標題在第 15 幀出現 */}
<Sequence from={15}>
<Title />
</Sequence>
{/* 副標題在第 30 幀出現 */}
<Sequence from={30}>
<Subtitle />
</Sequence>
{/* 行動呼籲在第 60 幀出現 */}
<Sequence from={60}>
<CallToAction />
</Sequence>
</AbsoluteFill>
);
};建立浮水印覆蓋層
import { AbsoluteFill, Img, staticFile } from "remotion";
export const WithWatermark: React.FC = () => {
return (
<AbsoluteFill>
{/* 主要內容 */}
<AbsoluteFill>
<MainContent />
</AbsoluteFill>
{/* 浮水印覆蓋層(最上層,不阻擋互動) */}
<AbsoluteFill
style={{
justifyContent: "flex-end",
alignItems: "flex-end",
padding: 24,
pointerEvents: "none",
}}
>
<Img
src={staticFile("watermark.png")}
style={{ width: 120, opacity: 0.7 }}
/>
</AbsoluteFill>
</AbsoluteFill>
);
};半透明遮罩效果
import { AbsoluteFill, OffthreadVideo, staticFile, useCurrentFrame, interpolate } from "remotion";
export const VideoWithOverlay: React.FC = () => {
const frame = useCurrentFrame();
// 影片播放到尾聲時漸出
const overlayOpacity = interpolate(frame, [80, 100], [0, 0.8], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<AbsoluteFill>
{/* 影片底層 */}
<AbsoluteFill>
<OffthreadVideo src={staticFile("clip.mp4")} />
</AbsoluteFill>
{/* 漸出的黑色遮罩 */}
<AbsoluteFill
style={{
backgroundColor: "black",
opacity: overlayOpacity,
}}
/>
{/* 結尾文字 */}
<AbsoluteFill
style={{
justifyContent: "center",
alignItems: "center",
opacity: interpolate(frame, [90, 100], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
}),
}}
>
<h1 style={{ color: "white", fontSize: 80 }}>感謝觀看</h1>
</AbsoluteFill>
</AbsoluteFill>
);
};常見的版面配置技巧
由於 <AbsoluteFill> 預設使用 display: flex 和 flexDirection: column,你可以直接用 flexbox 屬性來對齊內容:
// 置中對齊
<AbsoluteFill style={{ justifyContent: "center", alignItems: "center" }}>
<h1>完全置中</h1>
</AbsoluteFill>
// 靠右下角對齊
<AbsoluteFill style={{ justifyContent: "flex-end", alignItems: "flex-end", padding: 40 }}>
<span>右下角文字</span>
</AbsoluteFill>
// 橫向排列(覆蓋預設的 column 方向)
<AbsoluteFill style={{ flexDirection: "row", justifyContent: "space-around", alignItems: "center" }}>
<div>左側</div>
<div>右側</div>
</AbsoluteFill>相容性
| 環境 | 支援 |
|---|---|
| Chrome | 是 |
| Firefox | 是 |
| Safari | 是 |
| 客戶端渲染 | 是 |
| 伺服器端渲染 | 是 |
| Player | 是 |
| Studio | 是 |