Communitygithub.com

Guilhermeslvg/video-portfolio

內容、動畫與特效的最佳實踐指南

video-portfolio 是什麼?

此技能提供一套完整的指導方針,專為使用 Remotion 撰寫 React Markup 以製作影片、動畫及特效而設計。其核心目標是確保所有動畫與計時邏輯皆能與 Remotion Studio 相容,避免使用 CSS transition、animation 或 Tailwind 動畫類別,因為這些無法正確渲染。技能強調使用 useCurrentFrame() 與 interpolate() 驅動動畫,並善用 Easing.bezier() 與 Easing.spring() 自訂計時曲線。對於互動式元件,它推薦使用 Interactive.withSchema({wrapInSequence: true}),並指導如何透過 premountFor 屬性預先掛載媒體與計時元件,以確保在不同影格率下仍能正確運作。此外,技能還涵蓋了素材管理、媒體元件(Video、Audio、CanvasImage、AnimatedImage)的使用、場景範例、修剪與延遲、地圖整合、文字高亮與註解、多場景影片、連結構圖、語音旁白、嵌入影片/音訊、裁切、轉場、動態模糊、視覺與像素特效等多個進階主題,並提供具體的程式碼範例。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Guilhermeslvg/video-portfolio/tree/HEAD/.agents/skills/remotion-markup

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

預覽

來自技能 README

說明文件

這是撰寫 Remotion React Markup 的指導方針。 如果這不相關,請改為載入 Remotion 最佳實踐。

保留使用者的修改

使用者在對話之外可能會對程式碼進行編輯。

如果您發現程式碼在此期間有令人意外的變更,請不要覆寫它,假設這是刻意的,或請求確認。

通用規則

在合成或場景元件內部,從 useVideoConfig() 取得 fps,用於所有「秒數轉影格」的計算,包括計時屬性,例如 from、trimBefore、durationInFrames 和 premountFor。將表達式保留在 JSX 節點上(例如 trimBefore={4 * fps}),可讓 Studio 編輯計時,並在合成影格率變更時保持正確。

使用 useCurrentFrame() 和 interpolate() 驅動動畫。 CSS transition 或 animation 無法正確渲染,需要重構。 Tailwind 動畫類別無法正確渲染,需要重構。

使用 Easing.bezier() 和 Easing.spring() 自訂計時。

根據 Remotion 互動性最佳實踐來組織您的標記。 對於具有可編輯屬性的場景,偏好使用 Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'})。其內部標記可以回傳一個片段;包裝器會處理佈局和實例樣式。較小的元素(如下方三分之一)可以保留預設佈局,並將 style 轉發到它們自己的根元素。將可重複使用的場景註冊為已連結構圖。 將計時直接放在支援它的元件上;避免使用冗餘的 <Sequence> 包裝器。 為每個支援 premountFor 的計時元件提供一秒的預掛載時間:premountFor={fps},其中 fps 來自 useVideoConfig()。將此應用於媒體、互動式元件、<Sequence>、<TransitionSeries.Sequence> 和 <TransitionSeries.Overlay>,包括嵌套在場景內的計時元件。當嵌套項目需要在父項目開始之前掛載時,也要預掛載父時間軸項目。一個沒有 premountFor 的元件(例如 <TransitionSeries.Transition>)則無需替代方案。

Studio 會編輯建立項目的 JSX 原始碼節點。每個合成註冊、片段、場景、圖層和序列都應作為獨立的 JSX 節點進行編寫,並內嵌其可編輯的屬性。程式化迴圈適用於生成的實例被刻意控制為單一來源模板的情況,而不是使用者需要單獨編輯實例的情況。

import { useCurrentFrame, useVideoConfig, Easing, interpolate, Interactive } from "remotion";

export const FadeIn = () => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  return (
    <Interactive.Div
      name="Title"
      premountFor={fps}
      style={{
        opacity: interpolate(frame, [0, 2 * fps], [0, 1], {
          extrapolateRight: "clamp",
          extrapolateLeft: "clamp",
          easing: Easing.bezier(0.16, 1, 0.3, 1),
        }),
      }}
    >
      Hello World!
    </Interactive.Div>
  );
};

將 interpolate() 呼叫內聯在 style 屬性中。 使用 scale、translate、rotate 等 CSS 屬性,而不是 transform。

// 👍 內聯可編輯的關鍵影格和轉換簡寫
style={{
  scale: interpolate(frame, [0, 100], [0, 1], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
    output: 'perceptual-scale' // 對於 `scale` 動畫,使用 "output: 'perceptual-scale'"
  }),
  translate: interpolate(frame, [0, 100], ["0px 0px", "100px 100px"], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
  }),
  rotate: interpolate(frame, [0, 100], ["20deg", "90deg"], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
  }),
}}

// 👎 非內聯的值和轉換字串在 Studio 中變得更難編輯
const scale = interpolate(frame, [0, 100], [0, 1]);

style={{
  transform: `scale(${scale})`,
}}

素材

將素材放置在專案根目錄的 public/ 資料夾中。 使用 staticFile() 來引用 public/ 資料夾中的檔案。

媒體元件

使用來自 @remotion/media 的 <Video> 和 <Audio> 加入影片和音訊。 使用 <CanvasImage> 元件加入圖片。 使用 <AnimatedImage> 加入動畫 GIF、APNG、WebP 或 AVIF 圖片;如果不是使用 Chrome,請使用 @remotion/gif。 使用 staticFile() 處理 public/ 中的檔案,或直接傳遞遠端 URL:

import { Audio, Video } from "@remotion/media";
import { staticFile, CanvasImage, AnimatedImage, useVideoConfig } from "remotion";

export const MyComposition = () => {
  const {fps} = useVideoConfig();

  return (
    <>
      <Video src={staticFile("video.mp4")} premountFor={fps} style={{ opacity: 0.5 }} />
      <Audio src={staticFile("audio.mp3")} premountFor={fps} />
      <CanvasImage
        src={staticFile("logo.png")}
        premountFor={fps}
        style={{ width: 100, height: 100 }}
      />
      <Video src="https://remotion.media/video.mp4" premountFor={fps} />
      <AnimatedImage src={staticFile('nyancat.gif')} premountFor={fps} />
    </>
  );
};

如果合成主要是一個影片或音訊片段的時間軸,請在選擇其來源結構之前先閱讀 video-editing.md。

場景範例

一個帶有下方三分之一區塊的背景影片。 下方三分之一是一個互動式元件,擁有自己的時間軸,並註冊為連結構圖。 其文字作為 children 傳遞,而 accentColor 是一個可編輯的屬性。淡入效果在呼叫處以內聯方式設定關鍵影格。

// MyScene.tsx
import { Video } from "@remotion/media";
import { Easing, interpolate, useCurrentFrame, useVideoConfig } from "remotion";
import { LowerThird } from "./LowerThird";

export const MyScene = () => {
  const { fps } = useVideoConfig();
  const frame = useCurrentFrame();

  return (
    <>
      <Video
        name="Background"
        src="https://remotion.media/video.mp4"
        premountFor={fps}
        objectFit="cover"
        style={{
          position: "absolute",
          width: "100%",
          height: "100%",
        }}
      />
      <LowerThird
        name="Lower third"
        from={1 * fps}
        premountFor={fps}
        accentColor="#0b84f3"
        style={{
          opacity: interpolate(frame, [1 * fps, 2 * fps], [0, 1], {
            extrapolateRight: "clamp",
            extrapolateLeft: "clamp",
            easing: Easing.bezier(0.16, 1, 0.3, 1),
          }),
        }}
      >
        Jane Doe, Product Designer
      </LowerThird>
    </>
  );
};
// LowerThird.tsx
import type React from "react";
import { Interactive, type InteractivitySchema } from "remotion";

type LowerThirdProps = {
  readonly children: React.ReactNode;
  readonly accentColor: string;
  readonly style?: React.CSSProperties;
};

const LowerThirdInner: React.FC<LowerThirdProps> = ({
  children,
  accentColor,
  style,
}) => {
  return (
    <Interactive.Div
      style={{
        position: "absolute",
        left: 80,
        bottom: 80,
        display: "flex",
        alignItems: "center",
        gap: 20,
        backgroundColor: "white",
        borderRadius: 16,
        padding: "20px 32px",
        color: "black",
        fontFamily: "Helvetica, Arial, sans-serif",
        fontSize: 48,
        fontWeight: 600,
        ...style,
      }}
    >
      <div
        style={{
          width: 8,
          alignSelf: "stretch",
          borderRadius: 4,
          backgroundColor: accentColor,
        }}
      />
      {children}
    </Interactive.Div>
  );
};

const lowerThirdSchema = {
  ...Interactive.childrenSchema,
  accentColor: {
    type: "color",
    default: "#0b84f3",
    description: "強調色",
  },
} as const satisfies InteractivitySchema;

export const LowerThird = Interactive.withSchema({
  Component: LowerThirdInner,
  componentName: "LowerThird",
  schema: lowerThirdSchema,
  wrapInSequence: true,
});
import { Composition } from "remotion";
import { LowerThird } from "./LowerThird";
import { MyScene } from "./MyScene";

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="MyScene"
        component={MyScene}
        durationInFrames={60}
        fps={30}
        width={1280}
        height={720}
      />
      <Composition
        id="LowerThird"
        component={LowerThird}
        durationInFrames={30}
        fps={30}
        width={1280}
        height={720}
        defaultProps={{
          children: "Jane Doe, Product Designer",
          accentColor: "#0b84f3",
        }}
      />
    </>
  );
};

延遲與修剪

將計時直接放在支援它的元件上。有關支援的屬性、範例和操作順序,請參閱計時屬性。

地圖

如果希望在影片中包含地圖,請參閱 Remotion 地圖。

文字高亮與註解

有關文字高亮(高亮標記)、圓圈、底線、刪除線、劃掉文字、方框等,請參閱 text-highlights.md。

多場景影片

如果計劃製作包含多個連續場景的影片,請參閱 multi-scene-video.md。

連結構圖

當一個場景或一組圖層值得擁有自己的可編輯時間軸時,請遵循 connected-compositions.md。在多場景影片中,對於重要的場景,偏好使用此結構。

預先合成動作

對於 Studio 的請求,例如 Pre-compose Ambient glow (src/BarChart.tsx:134),在指定位置找到選定的序列標記。按照 connected-compositions.md 製作一個連結構圖:將標記提取到一個命名元件中,最好使用 Interactive.withSchema({wrapInSequence: true}) 使其具有互動性,並在根目錄中使用唯一的 <Composition> 註冊相同的匯出元件引用。直接使用其計時屬性渲染互動式元件,或者當該包裝器有特定用途時,將其作為序列的唯一子元件渲染。如果選定的節點已經是一個序列,則保留其屬性並提取其子元件。註冊需要與其父元件使用方式等效的尺寸、影格率、持續時間和 defaultProps。僅提取元件而沒有註冊合成,無法完成預先合成請求。

將繼承的樣式(例如 fontFamily 和字型載入)帶入提取的元件中;請參閱父元件獨立性。

語音旁白

有關使用 ElevenLabs TTS 為 Remotion 合成添加 AI 生成的語音旁白,請參閱 voiceover.md。

嵌入影片

有關嵌入影片的進階知識——修剪、音量、速度、循環、音調,請參閱 embedding-videos.md。

嵌入音訊

有關進階音訊功能,如修剪、音量、速度、音調,請參閱 audio.md。

裁切

如果需要裁切元件的可見矩形區域,請參閱 cropping.md。

轉場

有關場景轉場模式,請參閱 transitions.md。

動態模糊

在添加動態模糊或移動軌跡時,請閱讀 motion-blur.md 了解首選的 HTML-in-canvas 方法、預覽要求和替代方案。

視覺與像素特效

在添加視覺特效時,首先識別它應影響的元素或群組。 如果目標元件支援 effects 並且下面有匹配的內建特效,請優先使用該特效而非 CSS 近似效果。例如,當暈影效果應影響某個影片時,直接將 vignette() 應用於 <Video> 的 effects 屬性。

以下元件直接接受 effects:

從哪裡導入元件
remotion<Img>, <CanvasImage>, <AnimatedImage>, <Solid>, <HtmlInCanvas>
@remotion/media<Video>
@remotion/gif<Gif>
@remotion/rive<RemotionRiveCanvas>
@remotion/shapes<Arrow>, <Callout>, <Circle>, <Ellipse>, <Heart>, <Pie>, <Polygon>, <Rect>, <Spark>, <Star>, <Triangle>

普通的 HTML 元素、<Interactive.*> DOM 包裝器、<AbsoluteFill> 和 <Sequence> 不直接接受 effects。要對其內容應用特效,請將它們包裹在 <HtmlInCanvas> 中,並將 effects 屬性放在包裝器上。

對於其他元素,在適合的情況下,優先使用常規 HTML 和 CSS。當對整個群組進行後期處理是合適的且環境支援時,考慮將一個群組包裹在 <HtmlInCanvas> 中。

在應用特效之前,請閱讀 effects.md 以了解導入、參數和設定。當沒有匹配的內建特效時,它還涵蓋了使用 createEffect() 建立可重複使用的自訂特效。

可用特效

來自 @remotion/effects 的內建特效:

  • 顏色與色調:brightness(), colorCorrection(), colorKey(), contrast(), duotone(), exposure(), grayscale(), hue(), invert(), levels(), linearGradient(), linearGradientTint(), lut(), saturation(), shadowsHighlights(), thermalVision(), tint(), vibrance(), whiteBalance()。
  • 模糊與軌跡:blur(), lightTrail(), linearProgressiveBlur(), radialProgressiveBlur(), regionBlur

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

相關技能