Official视频与动画github.com

remotion-markup

内容、动画及效果最佳实践指南

remotion-markup 是什么?

该技能提供了一套全面的 Remotion React 标记编写指南,专注于内容布局、动画驱动和视觉效果,确保在 Remotion Studio 中可获得最佳编辑体验。它详细说明了如何通过 `useVideoConfig()` 获取 `fps` 进行计算,使用 `useCurrentFrame()` 和 `interpolate()` 驱动动画,并避免使用 CSS transition 或 animation 等不兼容的动画方式。技能强调使用 `Interactive.withSchema()` 构建可编辑的场景,并将 `interpolate()` 调用内联在 `style` prop 中,以便 Studio 能够直接编辑关键帧。此外,它还覆盖了媒体组件(视频、音频、图片)的使用、资源管理、场景交互、文本高亮、多场景视频、连接合成、转场效果、运动模糊以及内置视觉效果的全面参考,是开发复杂 Remotion 动画和视频项目的核心参考。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/remotion-dev/skills/tree/main/skills/remotion-markup

Installed? Explore more 视频与动画 skills: steipete/video-frames, steipete/summarize, openclaw/openclaw · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

预览

来自技能 README

文档

此文档提供了编写 Remotion React 标记的指导。 如果不相关,请加载 Remotion 最佳实践。

保留用户更改

用户可能会在对话之外对代码进行编辑。

如果在编辑过程中检测到意外的更改,请不要覆盖它,假定更改是故意的,或者请求确认。

通用规则

在合成(Composition)或场景(Scene)组件内部,请使用 useVideoConfig() 获取 fps 用于所有秒到帧的计算,包括 timing props 如 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'})。其内部标记可以返回一个片段(fragment);包装器处理布局和实例样式。对于像下三分之一(lower third)这样的有大小元素,可以保留默认布局并将 style 转发到其自身根节点。将可复用的场景注册为连接合成(connected compositions)。 将 timing 直接放在支持它的组件上;避免使用多余的 <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 prop 中。 优先使用 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> 组件添加图片。 对于动画 GIF、APNG、WebP 或 AVIF 图片,使用 <AnimatedImage> 组件;如果不使用 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",
        }}
      />
    </>
  );
};

延迟与裁剪

将 timing 直接放在支持它的组件上。有关支持的 props、示例和操作顺序,请参见 Timing props。

地图

如果需要在视频中包含地图,请参见 Remotion Maps。

文本高亮与注释

关于文本高亮(高亮标记)、圆圈、下划线、删除线、划掉文本、方框等,请参见 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> 注册相同的导出组件引用。直接使用其 timing props 渲染交互组件,或者当包装器有特定用途时,将其作为序列的唯一子元素渲染。如果选定的节点已经是一个序列,保留其属性并提取其子元素。注册时需要包含尺寸、fps、持续时间和等同于其父级使用的 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 近似。例如,当暗角效果应该影响某个 <Video> 时,直接将其 vignette() 应用到 <Video> 的 effects prop 上。

以下组件直接接受 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 prop 放在包装器上。

对于其他元素,在合适的情况下优先使用常规的 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()。
  • 失真和变形:bulge()、colorWander()、cube()、displacement()、glitch()、hexagonalPixelate()、mirror()。
  • 覆盖层和艺术效果:layers()、noise()、overlay()、pixelate()、sketch()。
  • 模糊和发光:glow()、dropShadow()、chromeKey()、chromaKey()。

有关可用的效果、参数和视觉预览的完整列表,请参见 effects.md。

Individual skills in this repo

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

相关技能