Communitygithub.com

jeffersonmendo/remotion-tryslides

结构化 Remotion 标记以实现交互性

remotion-tryslides 是什么?

该技能指导开发者如何编写 Remotion 标记,以使 Remotion Studio 能够识别代码结构并实现交互功能,例如通过点击选择元素、拖放、调整大小、旋转以及编辑 CSS 样式和关键帧。它详细介绍了使用 `Interactive.create()`、`Track` 和连接组合来构建可编辑组件的最佳实践,包括如何为组件定义 schema 字段、注册可复用组件以及将时序直接放在组件上。该技能还强调了为每个可编辑项使用独立的 JSX 节点、内联 CSS 样式、使用 `interpolate()` 进行动画处理以及使用 `Interactive.Path` 编辑 SVG 路径的重要性。它适用于所有 Remotion 项目,支持通过 Studio 界面实现更高效的视频编辑工作流,并提供了大量代码示例和避免常见陷阱的指导。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/jeffersonmendo/remotion-tryslides/tree/HEAD/.agents/skills/remotion-interactivity

在你喜欢的 AI 中提问

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

预览

来自技能 README

remotion-tryslides preview

文档

通过以特定方式编写 Remotion 标记,Remotion Studio 能够识别代码的结构并使其具有交互性:

  • 允许通过点击选择项目
  • 允许拖放、调整大小和旋转
  • 编辑 CSS 样式
  • 使关键帧和缓动值可编辑

如果标记对于 Studio 来说过于复杂而无法使其交互,则相关值将变为灰色。

优先使用带有自身时间线的交互式组件

使用 Track、连接组合和 Interactive.create() 构建新的合成。

对于场景,明确将 layout 设置为 'absolute-fill';对于有界元素,设置为 'none'。使用 Composition 注册相同的导出场景和可复用元素组件。

首先查看完整的 Track + 连接组合示例。 它包括元素、场景、父时间线和根注册。

import {Track, useVideoConfig} from 'remotion';
import {Chapter} from './Chapter';

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

  return (
    <Track name="Chapters">
      <Chapter
        name="Opening"
        durationInFrames={3 * fps}
        title="Introduction"
      />
      <Chapter
        name="Feature"
        from={3 * fps}
        durationInFrames={4 * fps}
        title="New features"
      />
    </Track>
  );
};

将背景和对齐样式放在每个场景实例上,如完整示例所示。将仅用于布局的容器保留为纯 HTML。

选择 Schema 字段

Schema 中的每个键都是一个 prop,Studio 可以在调用站点读取和编辑它。 不在 schema 中的 Props 仍然有效,但在 Studio 中不可编辑。 键可以使用点表示法,例如 style.color。

类型用途可关键帧化
string字符串,例如标题、提示或代码片段否
number数字,可选 min、max、step、integer是
boolean开/关切换是(保持)
colorCSS 颜色字符串是
enum在 variants 之间选择,每个变体都有自己的嵌套 schema可选(保持)
array数字、颜色、枚举等的列表否
asset媒体源,可选 assetType否
font-familyCSS 字体系列否
font-weight字体粗细是
translate、scale、rotation-css、rotation-degrees、transform-origin变换是
uv-coordinate元素上的标准化 [x, y] 点是
svg-pathSVG 路径数据是
remotion-captionsCaption[] 数据否
hidden保持在控件之外的值—

每个字段都需要一个 default,并且应该有一个 description,Studio 将其显示为标签。 设置 keyframable: false 以显示静态控件。

公开内容和每个实例的外观,例如文本和强调色。 不要为 style.translate、style.scale、style.rotate、style.transformOrigin 和 style.opacity 添加字段:Interactive.create() 会自动添加它们。

要使根的 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。 它提供对子元素内容的控制,并可能在未来支持更丰富的内容。 目前,它使用一个标有“Text”的字符串字段,默认值为 ''。 如果组件只需要字符串子元素,请改为声明一个显式的 children 字段,其 type 为 'string'。 Interactive.textSchema 控制排版样式;childrenSchema 控制内容。 子元素片段不包含在 baseSchema 中,也不会自动添加到自定义组件中。 嵌套标记和计算出的子元素当前是只读的。 text-content 是 string 字段类型的已弃用别名。 有关所有选项,请参见 InteractivitySchema。

将可复用组件注册为连接组合

将场景和可复用元素注册为独立的组合。Studio 称这些为连接组合:它们可以在自己的时间线中打开,同时与父视频共享相同的组件实现。

在组件内部定义必要的样式和加载字体,以便它在没有父组件的情况下也能工作。请参阅父组件独立性。

注册父组件渲染的相同导出组件引用。对于上面的示例,使用 component={Chapter},而不是 ChapterInner 或内联包装器。 Interactive.create() 提供序列,因此不需要额外的 <Sequence> 来进行连接。

import {Composition} from 'remotion';
import {Chapter} from './Chapter';

export const RemotionRoot = () => (
  <Composition
    id="Chapter"
    component={Chapter}
    width={1280}
    height={720}
    fps={30}
    durationInFrames={90}
    defaultProps={{
      title: 'A new perspective',
      style: {
        backgroundColor: '#111111',
        justifyContent: 'center',
        alignItems: 'center',
      },
    }}
  />
);

选择唯一的 ID、具有代表性的内联默认值,以及适合预览组件的尺寸、fps 和时长。注册默认值不会自动从父实例复制 props。有关注册和提取的详细信息,请参阅连接组合。

将时序直接放在组件上

避免在已经接受时序 props 的组件周围使用单独的 <Sequence>。内置交互式组件和通过 Interactive.create() 制作的自定义组件可以直接接收 name、from、durationInFrames、trimBefore 和 playbackRate。

<Chapter
  name="Opening scene"
  from={30}
  durationInFrames={90}
  style={{opacity: 0.9}}
  title="A new perspective"
/>

对于共享场景时序,将内容分组到使用 Interactive.create() 制作的组件中,并为该实例设置时序。托管根也接受 width 和 height 用于 useVideoConfig()。

使用 <Track> 将项目放在同一 Studio 时间线行上。 当场景是顺序的时候,使用 <TransitionSeries>。

为每个可独立编辑的项目提供其自己的 JSX 节点

Studio 编辑创建项目的 JSX 源节点。如果多个运行时项目来自同一个 JSX 节点,它们共享一个源编辑目标。

对于每个应独立可编辑的组合注册、剪辑、场景、层或序列,编写一个单独的 JSX 节点,并将其可编辑的 props 保留在该节点上。这适用于 <Composition>、<Still>、内置媒体组件、<Sequence>、<TransitionSeries.Sequence> 和自定义组件。

例如,像这样编写一个可编辑的时间线:

import { Track } from "remotion";

<Track name="Scenes">
  <Introduction name="Introduction" durationInFrames={90} />
  <Demo name="Demo" from={90} durationInFrames={150} />
</Track>

.map() 或其他程序化循环会从一个 JSX 源节点创建多个运行时项目。这对于有意作为单个模板控制的重复输出(例如可视化条形图或粒子)是合适的。当实例在 Studio 中需要独立的 ID 或名称、props、元数据、时序、排序、删除或复制时,这不合适。

使用 Interactive 使 HTML 元素可交互

除了 <Img>(它已经是交互式的),每个 HTML 和 SVG 元素(例如 <div>)都可以使用 Interactive 变成交互式:

<Interactive.Div
  name="Greeting card"
  style={{fontSize: 80, padding: 24}}
>
  Hello
</Interactive.Div>

这允许在 Studio 中设置样式和关键帧。请合理使用,如果组件有很多元素,时间线可能会变得混乱。

优先使用内联文本

如果文本是固定的且只使用一次,请直接将其写在交互式元素内部,而不是将其提取为常量。

// 👍 固定文本保持可编辑
<Interactive.Div name="Title">
  Remotion Best Practices
</Interactive.Div>

仅在文本是动态的或可复用时使用 prop 或变量。

为交互式元素提供描述性名称

向元素添加 name prop,使其易于识别。避免使用计算出的名称,请硬编码它们。

<>
  <Interactive.Div name="Hero title" style={{fontSize: 80}}>
    Launch day
  </Interactive.Div>
  <Img name="Avatar" src="https://remotion.media/image.jpeg" />
  <Video name="Background" src="https://remotion.media/video.mp4" />
  <Interactive.Div name="Title">
    Launch day
  </Interactive.Div>
</>

保持所有 CSS 样式内联

最好的方法是直接将纯对象传递给 style——不要引用常量,不要展开对象,不要进行数学运算。

<Interactive.Div
  style={{
    fontSize: 80,
    color: 'red',
  }}
>
  Hello World!
</Interactive.Div>
const baseStyle = useMemo(() => {
  return {
    fontSize: 12 // ❌ 不支持非内联样式
  }
}, []);

<Interactive.Div
  style={{
    ...baseStyle, // ❌ 不支持展开
    color: RED, // ❌ 不支持引用常量
    scale: frame * 10 // ❌ 不支持数学运算
  }}
>
  Hello World!
</Interactive.Div>

使用 interpolate() 进行动画

将动画编写为在变化的属性上的内联 interpolate() 调用。 输出范围、缓动、外推和 output 属性应使用硬编码值。

输入范围还可以使用直接从 useVideoConfig() 解构的 durationInFrames、fps、width 和 height。支持裸标识符(例如 durationInFrames)、与数字相乘(例如 2 * fps 或 fps * 2)以及减去数字(例如 durationInFrames - 1)。

const {fps, durationInFrames} = useVideoConfig();

// 👍 内联值可以被标准化和关键帧化
<Interactive.Div
  name="Product card"
  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}),
        output: 'perceptual-scale',
        extrapolateLeft: 'clamp',
        extrapolateRight: 'clamp'
      }
    ),
  }}
/>
const translateY = interpolate(frame, [0, 30], [0, 120]); // ❌ 数学运算应直接在标记中

<Interactive.Div
  name="Product card"
  style={{
    translate: translateY, // ❌ 只支持内联 interpolate() 调用,
    rotate: interpolate(frame, [start, start + 10], [0, Math.PI]), // ❌ 不能对任意变量使用数学运算,不能使用常量
    scale: interpolate(anyVariable, [0, 30], [0, 1]) // ❌ 只能解释 `frame` 变量。
  }}
/>

使用 Interactive.Path 保持 SVG 路径可编辑

使用来自 remotion 的 <Interactive.Path> 而不是 SVG 内部的 <path>,以使路径在视觉上可编辑。

安装 @remotion/paths 以进行路径插值和 Studio 路径关键帧:

bunx remotion add @remotion/paths

如果几何图形是静态的,请将路径字符串直接放在 d prop 中。不要将其提取为常量。

import {Interactive} from 'remotion';

<Interactive.Svg width={300} height={300} viewBox="0 0 300 300">
  <Interactive.Path
    name="Triangle"
    d="M 40 40 L 260 40 L 150 260 Z"
    fill="#0b84f3"
  />
</Interactive.Svg>

使用内联路径进行变形

…

[译文截断]

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相关技能