关于 remotion-interactivity
remotion-interactivity 是一项技能,指导如何组织 Remotion 合成内容,使 Studio 能编辑时间线元素及其属性。它介绍基于架构的控件、交互元素、关键帧和可编辑视频时间线。
主要功能
- 01基于架构的可编辑控件
- 02可交互的文本与图形
- 03可编辑属性和关键帧
- 04视频时间线组织指南
适用场景
- 01让合成文本可在 Studio 中编辑
- 02添加按实例设置的样式控件
- 03构建片段可独立编辑的时间线
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ remotion-dev/ remotion --skill remotion-interactivity安装后在 Claude Code 中输入/remotion-interactivity确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
timeline-structure.mdinline-effects.mdagents/openai.yaml详细介绍
Studio 会编辑用于创建时间线项目的 JSX 节点。围绕用户应能独立选择、移动、裁剪或设置样式的内容来组织代码。将可编辑值内联放在每个实例上。
在编写场景或重构其图层之前,请阅读可编辑时间线结构。其中介绍了编辑单元、轨道、共享时间、普通布局容器,以及从组件到根节点的完整示例。对于由素材构建的时间线,还请阅读可编辑视频时间线。
当场景或可复用组需要拥有自己的时间线时,请使用连接的合成。请分别验证其独立视图和完整的父级时间线;仅建立连接并不能保证可见行数会减少。
选择架构字段
架构中的每个键都是 Studio 可在调用处读取和编辑的属性。不在架构中的属性仍然可用,但无法在 Studio 中编辑。键可以使用点表示法,例如 style.color。
| 类型 | 用途 | 可设关键帧 |
|---|---|---|
string | 字符串,例如标题、提示词或代码片段 | 否 |
number | 数字,可选设置 min、max、step、integer | 是 |
boolean | 开/关切换项 | 是(保持) |
color | CSS 颜色字符串 | 是 |
enum | 从多个 variants 中选择,每个选项都有自己的嵌套架构 | 可选(保持) |
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 添加字段:Interactive.create() 会自动添加这些字段。
若要让根元素的 CSS 属性可按实例编辑,请展开内置架构片段:
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。
它提供子元素内容的控件,并可能逐步支持更丰富的内容。
目前,它使用一个标记为“文本”、默认值为 '' 的字符串字段。
如果组件要求子元素只能是字符串,请改为显式声明 children 字段,并设置 type: 'string'。
Interactive.textSchema 控制排版样式;childrenSchema 控制内容。
子元素片段不会包含在 baseSchema 中,也不会自动添加到自定义组件中。
嵌套标记和计算得出的子元素目前为只读。
text-content 是已弃用的 string 字段类型别名。
所有选项请参阅 InteractivitySchema。
使用 Interactive 让 HTML 元素具有交互性
所有 HTML 和 SVG 元素(<Img> 除外,它本身已经具有交互性),例如 <div>,都可以通过 Interactive 变为可交互:
<Interactive.Div
name="Greeting card"
style={{fontSize: 80, padding: 24}}
>
Hello
</Interactive.Div>
当文本或图形需要独立控件时,请使用此方式。仅用于布局的容器和装饰性内部元素应保留为普通 HTML;可交互组件已为其根元素提供控件。
优先使用行内文本
如果文本是固定的且只使用一次,请直接写在可交互元素内部,而不是将其提取为常量。
// 👍 Fixed copy stays editable
<Interactive.Div name="Title">
Remotion Best Practices
</Interactive.Div>
只有在文本是动态的或会重复使用时,才使用属性或变量。
为可交互元素设置描述性名称
为元素添加 name 属性,使其易于识别。
避免使用计算得出的名称,直接写死名称。
<>
<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 // ❌ Non-inline styles are not supported
}
}, []);
<Interactive.Div
style={{
...baseStyle, // ❌ Spreading is not supported
color: RED, // ❌ Referring to constants is not supported
scale: frame * 10 // ❌ Math is not supported
}}
>
Hello World!
</Interactive.Div>
使用 interpolate() 制作动画
将动画写成行内 interpolate() 调用,并用于发生变化的属性。
输出范围、缓动、外推和 output 属性都应使用写死的值。
输入范围还可以使用从 useVideoConfig() 中直接解构的 durationInFrames、fps、width 和 height。支持裸标识符(例如 durationInFrames)、与数字相乘(例如 2 * fps 或 fps * 2),以及减去数字(例如 durationInFrames - 1)。
const {fps, durationInFrames} = useVideoConfig();
// 👍 Inline values can be standardized and keyframed
<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]); // ❌ Math should be directly in the markup
<Interactive.Div
name="Product card"
style={{
translate: translateY, // ❌ Only inline interpolate() calls are supported,
rotate: interpolate(frame, [start, start + 10], [0, Math.PI]), // ❌ Cannot use math with arbitrary variables, cannot use constants
scale: interpolate(anyVariable, [0, 30], [0, 1]) // ❌ Can only interpret the `frame` variable.
}}
/>
使用 Interactive.Path 保持 SVG 路径可编辑
在 SVG 内部使用来自 remotion 的 <Interactive.Path>,而不是 <path>,以便直观地编辑路径。
安装 @remotion/paths,以支持路径插值和 Studio 路径关键帧:
bunx remotion add @remotion/paths
如果几何形状是静态的,请将路径字符串直接写在 d 属性中。不要将其提取为常量。
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>
使用行内 interpolatePaths() 变形路径
直接在 d 中使用来自 @remotion/paths 的 interpolatePaths()。
它接受一个帧、一个输入范围、一个长度相同的路径字符串数组,以及用于缓动、外推和海报化的选项。
请将输出路径、范围和选项保持为行内写法,并遵循上文 interpolate() 的相同输入范围规则。
当路径关键帧需要在 Studio 中保持可编辑时,不要使用 interpolatePath() API,也不要将插值结果提取到变量中。
import {interpolatePaths} from '@remotion/paths';
import {Easing, Interactive, useCurrentFrame} from 'remotion';
export const MorphingPath = () => {
const frame = useCurrentFrame();
return (
<Interactive.Svg width={300} height={300} viewBox="0 0 300 300">
<Interactive.Path
name="Morphing triangle"
d={interpolatePaths(
frame,
[0, 30, 60],
[
'M 40 40 L 260 40 L 150 260 Z',
'M 40 150 L 150 40 L 260 150 Z',
'M 40 260 L 150 40 L 260 260 Z',
],
{
easing: Easing.bezier(0.42, 0, 0.58, 1),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
},
)}
fill="#0b84f3"
/>
</Interactive.Svg>
);
};
Studio 可以编辑当前帧的路径、添加或移动关键帧,并调整其缓动。
使用 strokeDasharray 和 strokeDashoffset 让路径逐渐显现。
使用 scale、translate、rotate CSS 属性
避免使用 transform CSS 属性。
如果可能,请改用 scale、rotate 和 translate,因为只有这些属性可以通过交互方式编辑。
将合成元数据保持为内联
创建合成时,id 使用 JSX 字符串字面量,将 width、height、fps、durationInFrames 和 defaultProps 保持为内联形式,并且不要进行类型断言。
如果 <Composition> 或 <Still> 上的 defaultProps 是内联对象字面量,Props 编辑器就可以将可视化编辑保存回代码。
// 👍 Static values are in <Composition>, dynamic values are in calculateMetadata()
const calculateMetadata = useMemo(async () => {
const dimensions = await getDimensions(); // just an example
return {width: dimensions.width, height: dimensions.height};
});
<Composition
id="my-video"
component={MyComponent}
durationInFrames={150}
fps={30}
calculateMetadata={calculateMetadata}
defaultProps={{title: 'Hello', color: '#0b84ff'}}
/>
const defaultProps = {title: 'Hello', color: '#0b84ff'}; // ❌ Don't extract defaultProps, must be inline
const calculateMetadata = useMemo(() => {
// ❌ Unnecessary because no calculation is being done,
return {durationInFrames: 150, fps: 30, width: 1920, height: 1080};
});
<Composition
id="my-video"
component={MyComponent}
calculateMetadata={calculateMetadata}
defaultProps={{
title: 'Hello',
} as Props} // ❌ Don't have type assertions, instead type MyComponent correctly
/>
对于动态元数据部分,只使用 calculateMetadata()。
效果也应保持为内联
将效果数组和参数保持为内联形式,以便其结构和关键帧仍可编辑。示例请参阅内联效果。
让自己的组件具有交互性
要让自定义的用户组件具有交互性,请使用: 让组件具有交互性
视频编辑
如果 Remotion 组件主要由视频和音频片段组成,请参阅视频编辑,了解如何组织 Remotion 标记,以便在时间轴中交互式地编辑片段。