dsh-remotion 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +29 -0
- package/README.md +29 -0
- package/cordis.patch.yml +7 -0
- package/lib/index.d.ts +35 -0
- package/lib/index.js +74 -0
- package/package.json +63 -0
- package/skills/remotion/SKILL.md +95 -0
- package/skills/remotion/rules/3d.md +86 -0
- package/skills/remotion/rules/animations.md +31 -0
- package/skills/remotion/rules/assets.md +78 -0
- package/skills/remotion/rules/audio-visualization.md +198 -0
- package/skills/remotion/rules/audio.md +169 -0
- package/skills/remotion/rules/calculate-metadata.md +134 -0
- package/skills/remotion/rules/can-decode.md +81 -0
- package/skills/remotion/rules/charts.md +120 -0
- package/skills/remotion/rules/compositions.md +154 -0
- package/skills/remotion/rules/display-captions.md +184 -0
- package/skills/remotion/rules/extract-frames.md +229 -0
- package/skills/remotion/rules/ffmpeg.md +34 -0
- package/skills/remotion/rules/fonts.md +152 -0
- package/skills/remotion/rules/get-audio-duration.md +58 -0
- package/skills/remotion/rules/get-video-dimensions.md +68 -0
- package/skills/remotion/rules/get-video-duration.md +60 -0
- package/skills/remotion/rules/gifs.md +141 -0
- package/skills/remotion/rules/images.md +134 -0
- package/skills/remotion/rules/import-srt-captions.md +69 -0
- package/skills/remotion/rules/light-leaks.md +73 -0
- package/skills/remotion/rules/lottie.md +70 -0
- package/skills/remotion/rules/maps.md +412 -0
- package/skills/remotion/rules/measuring-dom-nodes.md +34 -0
- package/skills/remotion/rules/measuring-text.md +140 -0
- package/skills/remotion/rules/parameters.md +109 -0
- package/skills/remotion/rules/sequencing.md +118 -0
- package/skills/remotion/rules/sfx.md +30 -0
- package/skills/remotion/rules/silence-detection.md +71 -0
- package/skills/remotion/rules/subtitles.md +36 -0
- package/skills/remotion/rules/tailwind.md +11 -0
- package/skills/remotion/rules/text-animations.md +20 -0
- package/skills/remotion/rules/timing.md +136 -0
- package/skills/remotion/rules/transcribe-captions.md +70 -0
- package/skills/remotion/rules/transitions.md +197 -0
- package/skills/remotion/rules/transparent-videos.md +106 -0
- package/skills/remotion/rules/trimming.md +51 -0
- package/skills/remotion/rules/videos.md +171 -0
- package/skills/remotion/rules/voiceover.md +99 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: measuring-text
|
|
3
|
+
description: Measuring text dimensions, fitting text to containers, and checking overflow
|
|
4
|
+
metadata:
|
|
5
|
+
tags: measure, text, layout, dimensions, fitText, fillTextBox
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Measuring text in Remotion
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
Install @remotion/layout-utils if it is not already installed:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx remotion add @remotion/layout-utils
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Measuring text dimensions
|
|
19
|
+
|
|
20
|
+
Use `measureText()` to calculate the width and height of text:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { measureText } from "@remotion/layout-utils";
|
|
24
|
+
|
|
25
|
+
const { width, height } = measureText({
|
|
26
|
+
text: "Hello World",
|
|
27
|
+
fontFamily: "Arial",
|
|
28
|
+
fontSize: 32,
|
|
29
|
+
fontWeight: "bold",
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Results are cached - duplicate calls return the cached result.
|
|
34
|
+
|
|
35
|
+
## Fitting text to a width
|
|
36
|
+
|
|
37
|
+
Use `fitText()` to find the optimal font size for a container:
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { fitText } from "@remotion/layout-utils";
|
|
41
|
+
|
|
42
|
+
const { fontSize } = fitText({
|
|
43
|
+
text: "Hello World",
|
|
44
|
+
withinWidth: 600,
|
|
45
|
+
fontFamily: "Inter",
|
|
46
|
+
fontWeight: "bold",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
style={{
|
|
52
|
+
fontSize: Math.min(fontSize, 80), // Cap at 80px
|
|
53
|
+
fontFamily: "Inter",
|
|
54
|
+
fontWeight: "bold",
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
Hello World
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Checking text overflow
|
|
63
|
+
|
|
64
|
+
Use `fillTextBox()` to check if text exceeds a box:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { fillTextBox } from "@remotion/layout-utils";
|
|
68
|
+
|
|
69
|
+
const box = fillTextBox({ maxBoxWidth: 400, maxLines: 3 });
|
|
70
|
+
|
|
71
|
+
const words = ["Hello", "World", "This", "is", "a", "test"];
|
|
72
|
+
for (const word of words) {
|
|
73
|
+
const { exceedsBox } = box.add({
|
|
74
|
+
text: word + " ",
|
|
75
|
+
fontFamily: "Arial",
|
|
76
|
+
fontSize: 24,
|
|
77
|
+
});
|
|
78
|
+
if (exceedsBox) {
|
|
79
|
+
// Text would overflow, handle accordingly
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Best practices
|
|
86
|
+
|
|
87
|
+
**Load fonts first:** Only call measurement functions after fonts are loaded.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { loadFont } from "@remotion/google-fonts/Inter";
|
|
91
|
+
|
|
92
|
+
const { fontFamily, waitUntilDone } = loadFont("normal", {
|
|
93
|
+
weights: ["400"],
|
|
94
|
+
subsets: ["latin"],
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
waitUntilDone().then(() => {
|
|
98
|
+
// Now safe to measure
|
|
99
|
+
const { width } = measureText({
|
|
100
|
+
text: "Hello",
|
|
101
|
+
fontFamily,
|
|
102
|
+
fontSize: 32,
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Use validateFontIsLoaded:** Catch font loading issues early:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
measureText({
|
|
111
|
+
text: "Hello",
|
|
112
|
+
fontFamily: "MyCustomFont",
|
|
113
|
+
fontSize: 32,
|
|
114
|
+
validateFontIsLoaded: true, // Throws if font not loaded
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Match font properties:** Use the same properties for measurement and rendering:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
const fontStyle = {
|
|
122
|
+
fontFamily: "Inter",
|
|
123
|
+
fontSize: 32,
|
|
124
|
+
fontWeight: "bold" as const,
|
|
125
|
+
letterSpacing: "0.5px",
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const { width } = measureText({
|
|
129
|
+
text: "Hello",
|
|
130
|
+
...fontStyle,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return <div style={fontStyle}>Hello</div>;
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Avoid padding and border:** Use `outline` instead of `border` to prevent layout differences:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
<div style={{ outline: "2px solid red" }}>Text</div>
|
|
140
|
+
```
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: parameters
|
|
3
|
+
description: Make a video parametrizable by adding a Zod schema
|
|
4
|
+
metadata:
|
|
5
|
+
tags: parameters, zod, schema
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
To make a video parametrizable, a Zod schema can be added to a composition.
|
|
9
|
+
|
|
10
|
+
First, `zod` must be installed .
|
|
11
|
+
|
|
12
|
+
Search the project for lockfiles and run the correct command depending on the package manager:
|
|
13
|
+
|
|
14
|
+
If `package-lock.json` is found, use the following command:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm i zod
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If `bun.lockb` is found, use the following command:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bun i zod
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
If `yarn.lock` is found, use the following command:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
yarn add zod
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If `pnpm-lock.yaml` is found, use the following command:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm i zod
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Then, a Zod schema can be defined alongside the component:
|
|
39
|
+
|
|
40
|
+
```tsx title="src/MyComposition.tsx"
|
|
41
|
+
import { z } from "zod";
|
|
42
|
+
|
|
43
|
+
export const MyCompositionSchema = z.object({
|
|
44
|
+
title: z.string(),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const MyComponent: React.FC<z.infer<typeof MyCompositionSchema>> = () => {
|
|
48
|
+
return (
|
|
49
|
+
<div>
|
|
50
|
+
<h1>{props.title}</h1>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
In the root file, the schema can be passed to the composition:
|
|
57
|
+
|
|
58
|
+
```tsx title="src/Root.tsx"
|
|
59
|
+
import { Composition } from "remotion";
|
|
60
|
+
import { MycComponent, MyCompositionSchema } from "./MyComposition";
|
|
61
|
+
|
|
62
|
+
export const RemotionRoot = () => {
|
|
63
|
+
return (
|
|
64
|
+
<Composition
|
|
65
|
+
id="MyComposition"
|
|
66
|
+
component={MyComponent}
|
|
67
|
+
durationInFrames={100}
|
|
68
|
+
fps={30}
|
|
69
|
+
width={1080}
|
|
70
|
+
height={1080}
|
|
71
|
+
defaultProps={{ title: "Hello World" }}
|
|
72
|
+
schema={MyCompositionSchema}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Now, the user can edit the parameter visually in the sidebar.
|
|
79
|
+
|
|
80
|
+
All schemas that are supported by Zod are supported by Remotion.
|
|
81
|
+
|
|
82
|
+
Remotion requires that the top-level type is a z.object(), because the collection of props of a React component is always an object.
|
|
83
|
+
|
|
84
|
+
## Color picker
|
|
85
|
+
|
|
86
|
+
For adding a color picker, use `zColor()` from `@remotion/zod-types`.
|
|
87
|
+
|
|
88
|
+
If it is not installed, use the following command:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx remotion add @remotion/zod-types # If project uses npm
|
|
92
|
+
bunx remotion add @remotion/zod-types # If project uses bun
|
|
93
|
+
yarn remotion add @remotion/zod-types # If project uses yarn
|
|
94
|
+
pnpm exec remotion add @remotion/zod-types # If project uses pnpm
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Then import `zColor` from `@remotion/zod-types`:
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { zColor } from "@remotion/zod-types";
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Then use it in the schema:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
export const MyCompositionSchema = z.object({
|
|
107
|
+
color: zColor(),
|
|
108
|
+
});
|
|
109
|
+
```
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sequencing
|
|
3
|
+
description: Sequencing patterns for Remotion - delay, trim, limit duration of items
|
|
4
|
+
metadata:
|
|
5
|
+
tags: sequence, series, timing, delay, trim
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Use `<Sequence>` to delay when an element appears in the timeline.
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { Sequence } from "remotion";
|
|
12
|
+
|
|
13
|
+
const {fps} = useVideoConfig();
|
|
14
|
+
|
|
15
|
+
<Sequence from={1 * fps} durationInFrames={2 * fps} premountFor={1 * fps}>
|
|
16
|
+
<Title />
|
|
17
|
+
</Sequence>
|
|
18
|
+
<Sequence from={2 * fps} durationInFrames={2 * fps} premountFor={1 * fps}>
|
|
19
|
+
<Subtitle />
|
|
20
|
+
</Sequence>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This will by default wrap the component in an absolute fill element.
|
|
24
|
+
If the items should not be wrapped, use the `layout` prop:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
<Sequence layout="none">
|
|
28
|
+
<Title />
|
|
29
|
+
</Sequence>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Premounting
|
|
33
|
+
|
|
34
|
+
This loads the component in the timeline before it is actually played.
|
|
35
|
+
Always premount any `<Sequence>`!
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<Sequence premountFor={1 * fps}>
|
|
39
|
+
<Title />
|
|
40
|
+
</Sequence>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Series
|
|
44
|
+
|
|
45
|
+
Use `<Series>` when elements should play one after another without overlap.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { Series } from "remotion";
|
|
49
|
+
|
|
50
|
+
<Series>
|
|
51
|
+
<Series.Sequence durationInFrames={45}>
|
|
52
|
+
<Intro />
|
|
53
|
+
</Series.Sequence>
|
|
54
|
+
<Series.Sequence durationInFrames={60}>
|
|
55
|
+
<MainContent />
|
|
56
|
+
</Series.Sequence>
|
|
57
|
+
<Series.Sequence durationInFrames={30}>
|
|
58
|
+
<Outro />
|
|
59
|
+
</Series.Sequence>
|
|
60
|
+
</Series>;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Same as with `<Sequence>`, the items will be wrapped in an absolute fill element by default when using `<Series.Sequence>`, unless the `layout` prop is set to `none`.
|
|
64
|
+
|
|
65
|
+
### Series with overlaps
|
|
66
|
+
|
|
67
|
+
Use negative offset for overlapping sequences:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
<Series>
|
|
71
|
+
<Series.Sequence durationInFrames={60}>
|
|
72
|
+
<SceneA />
|
|
73
|
+
</Series.Sequence>
|
|
74
|
+
<Series.Sequence offset={-15} durationInFrames={60}>
|
|
75
|
+
{/* Starts 15 frames before SceneA ends */}
|
|
76
|
+
<SceneB />
|
|
77
|
+
</Series.Sequence>
|
|
78
|
+
</Series>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Frame References Inside Sequences
|
|
82
|
+
|
|
83
|
+
Inside a Sequence, `useCurrentFrame()` returns the local frame (starting from 0):
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
<Sequence from={60} durationInFrames={30}>
|
|
87
|
+
<MyComponent />
|
|
88
|
+
{/* Inside MyComponent, useCurrentFrame() returns 0-29, not 60-89 */}
|
|
89
|
+
</Sequence>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Nested Sequences
|
|
93
|
+
|
|
94
|
+
Sequences can be nested for complex timing:
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
<Sequence from={0} durationInFrames={120}>
|
|
98
|
+
<Background />
|
|
99
|
+
<Sequence from={15} durationInFrames={90} layout="none">
|
|
100
|
+
<Title />
|
|
101
|
+
</Sequence>
|
|
102
|
+
<Sequence from={45} durationInFrames={60} layout="none">
|
|
103
|
+
<Subtitle />
|
|
104
|
+
</Sequence>
|
|
105
|
+
</Sequence>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Nesting compositions within another
|
|
109
|
+
|
|
110
|
+
To add a composition within another composition, you can use the `<Sequence>` component with a `width` and `height` prop to specify the size of the composition.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<AbsoluteFill>
|
|
114
|
+
<Sequence width={COMPOSITION_WIDTH} height={COMPOSITION_HEIGHT}>
|
|
115
|
+
<CompositionComponent />
|
|
116
|
+
</Sequence>
|
|
117
|
+
</AbsoluteFill>
|
|
118
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sfx
|
|
3
|
+
description: Including sound effects
|
|
4
|
+
metadata:
|
|
5
|
+
tags: sfx, sound, effect, audio
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
To include a sound effect, use the `<Audio>` tag:
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { Audio } from "@remotion/sfx";
|
|
12
|
+
|
|
13
|
+
<Audio src={"https://remotion.media/whoosh.wav"} />;
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The following sound effects are available:
|
|
17
|
+
|
|
18
|
+
- `https://remotion.media/whoosh.wav`
|
|
19
|
+
- `https://remotion.media/whip.wav`
|
|
20
|
+
- `https://remotion.media/page-turn.wav`
|
|
21
|
+
- `https://remotion.media/switch.wav`
|
|
22
|
+
- `https://remotion.media/mouse-click.wav`
|
|
23
|
+
- `https://remotion.media/shutter-modern.wav`
|
|
24
|
+
- `https://remotion.media/shutter-old.wav`
|
|
25
|
+
- `https://remotion.media/ding.wav`
|
|
26
|
+
- `https://remotion.media/bruh.wav`
|
|
27
|
+
- `https://remotion.media/vine-boom.wav`
|
|
28
|
+
- `https://remotion.media/windows-xp-error.wav`
|
|
29
|
+
|
|
30
|
+
For more sound effects, search the internet. A good resource is https://github.com/kapishdima/soundcn/tree/main/assets.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: silence-detection
|
|
3
|
+
description: Adaptive silence detection for video/audio files using FFmpeg loudnorm and silencedetect
|
|
4
|
+
metadata:
|
|
5
|
+
tags: silence, detection, trimming, ffmpeg, loudnorm, audio
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Adaptive Silence Detection
|
|
9
|
+
|
|
10
|
+
Detect silent segments in video or audio files.
|
|
11
|
+
|
|
12
|
+
Requires FFmpeg — see [ffmpeg.md](./ffmpeg.md) for how to invoke it in Remotion projects.
|
|
13
|
+
|
|
14
|
+
## Step 1: Measure loudness with `loudnorm`
|
|
15
|
+
|
|
16
|
+
Use the `loudnorm` filter in JSON mode to get the EBU R128 integrated loudness and gating threshold for each file:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx remotion ffmpeg -i public/video.mov -map 0:a -af loudnorm=print_format=json -f null /dev/null
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
As output you will get:
|
|
23
|
+
- `input_i`: Integrated loudness (dB) — the overall perceived volume
|
|
24
|
+
- `input_thresh`: EBU R128 gating threshold (dB) — the level below which audio is considered too quiet to count toward loudness measurement
|
|
25
|
+
|
|
26
|
+
## Step 2: Detect silences using adaptive threshold
|
|
27
|
+
|
|
28
|
+
Pass the `input_thresh` value from step 1 as the `noise` parameter to `silencedetect`:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx remotion ffmpeg -i public/video.mov -map 0:a -af "silencedetect=noise=${THRESH}dB:d=0.5" -f null /dev/null
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Parameters:
|
|
35
|
+
- `noise`: The threshold below which audio is considered silent. Use `input_thresh` from step 1.
|
|
36
|
+
- `d`: Minimum silence duration in seconds. `0.5` is a good default.
|
|
37
|
+
|
|
38
|
+
## Interpreting the output
|
|
39
|
+
|
|
40
|
+
The filter outputs pairs of `silence_start` and `silence_end` timestamps:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
[silencedetect] silence_start: 0
|
|
44
|
+
[silencedetect] silence_end: 2.241021 | silence_duration: 2.241021
|
|
45
|
+
[silencedetect] silence_start: 38.77425
|
|
46
|
+
[silencedetect] silence_end: 39.619604 | silence_duration: 0.845354
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Identifying leading and trailing silence
|
|
50
|
+
|
|
51
|
+
- **Leading silence**: Consecutive silence segments starting at or near 0. If the first `silence_start` is > 0.5s, there is no leading silence.
|
|
52
|
+
- **Trailing silence**: The last silence segment that extends to (or near) the end of the file. Compare the last `silence_end` with the file's total duration.
|
|
53
|
+
|
|
54
|
+
When multiple silences are nearly contiguous at the start or end (gap < 0.2s), treat them as a single leading/trailing silence block.
|
|
55
|
+
|
|
56
|
+
## Using with Remotion's `<Video>` component
|
|
57
|
+
|
|
58
|
+
Apply the detected trim points using `trimBefore` and `trimAfter` (values are in frames):
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { Video } from "@remotion/media";
|
|
62
|
+
import { staticFile, useVideoConfig } from "remotion";
|
|
63
|
+
|
|
64
|
+
const { fps } = useVideoConfig();
|
|
65
|
+
|
|
66
|
+
<Video
|
|
67
|
+
src={staticFile("video.mov")}
|
|
68
|
+
trimBefore={Math.floor(leadingEnd * fps)}
|
|
69
|
+
trimAfter={Math.ceil(trailingStart * fps)}
|
|
70
|
+
/>
|
|
71
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: subtitles
|
|
3
|
+
description: subtitles and caption rules
|
|
4
|
+
metadata:
|
|
5
|
+
tags: subtitles, captions, remotion, json
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
All captions must be processed in JSON. The captions must use the `Caption` type which is the following:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import type { Caption } from "@remotion/captions";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
This is the definition:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
type Caption = {
|
|
18
|
+
text: string;
|
|
19
|
+
startMs: number;
|
|
20
|
+
endMs: number;
|
|
21
|
+
timestampMs: number | null;
|
|
22
|
+
confidence: number | null;
|
|
23
|
+
};
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Generating captions
|
|
27
|
+
|
|
28
|
+
To transcribe video and audio files to generate captions, load the [./transcribe-captions.md](./transcribe-captions.md) file for more instructions.
|
|
29
|
+
|
|
30
|
+
## Displaying captions
|
|
31
|
+
|
|
32
|
+
To display captions in your video, load the [./display-captions.md](./display-captions.md) file for more instructions.
|
|
33
|
+
|
|
34
|
+
## Importing captions
|
|
35
|
+
|
|
36
|
+
To import captions from a .srt file, load the [./import-srt-captions.md](./import-srt-captions.md) file for more instructions.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tailwind
|
|
3
|
+
description: Using TailwindCSS in Remotion.
|
|
4
|
+
metadata:
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You can and should use TailwindCSS in Remotion, if TailwindCSS is installed in the project.
|
|
8
|
+
|
|
9
|
+
Don't use `transition-*` or `animate-*` classes - always animate using the `useCurrentFrame()` hook.
|
|
10
|
+
|
|
11
|
+
Tailwind must be installed and enabled first in a Remotion project - fetch https://www.remotion.dev/docs/tailwind using WebFetch for instructions.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: text-animations
|
|
3
|
+
description: Typography and text animation patterns for Remotion.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: typography, text, typewriter, highlighter ken
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Text animations
|
|
9
|
+
|
|
10
|
+
Based on `useCurrentFrame()`, reduce the string character by character to create a typewriter effect.
|
|
11
|
+
|
|
12
|
+
## Typewriter Effect
|
|
13
|
+
|
|
14
|
+
See [Typewriter](assets/text-animations-typewriter.tsx) for an advanced example with a blinking cursor and a pause after the first sentence.
|
|
15
|
+
|
|
16
|
+
Always use string slicing for typewriter effects. Never use per-character opacity.
|
|
17
|
+
|
|
18
|
+
## Word Highlighting
|
|
19
|
+
|
|
20
|
+
See [Word Highlight](assets/text-animations-word-highlight.tsx) for an example for how a word highlight is animated, like with a highlighter pen.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: timing
|
|
3
|
+
description: Interpolation and timing in Remotion—prefer interpolate with Bézier easing; springs as a specialized option
|
|
4
|
+
metadata:
|
|
5
|
+
tags: easing, bezier, interpolation, spring, timing
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Drive motion with `interpolate()` over explicit frame range. To customize timing, use **`Easing.bezier`**. The four parameters are the same as CSS `cubic-bezier(x1, y1, x2, y2)`.
|
|
9
|
+
|
|
10
|
+
A simple linear interpolation is done using the `interpolate` function.
|
|
11
|
+
|
|
12
|
+
```ts title="Going from 0 to 1 over 100 frames"
|
|
13
|
+
import { interpolate } from "remotion";
|
|
14
|
+
|
|
15
|
+
const opacity = interpolate(frame, [0, 100], [0, 1]);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
By default, the values are not clamped, so the value can go outside the range [0, 1].
|
|
19
|
+
Here is how they can be clamped:
|
|
20
|
+
|
|
21
|
+
```ts title="Going from 0 to 1 over 100 frames with extrapolation"
|
|
22
|
+
const opacity = interpolate(frame, [0, 100], [0, 1], {
|
|
23
|
+
extrapolateRight: "clamp",
|
|
24
|
+
extrapolateLeft: "clamp",
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Bézier easing
|
|
29
|
+
|
|
30
|
+
Use `Easing.bezier(x1, y1, x2, y2)` inside the `interpolate` options object. The curve is identical in spirit to CSS animations and transitions, which helps when you are stealing timing from the web or from a designer’s spec.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { interpolate, Easing } from "remotion";
|
|
34
|
+
|
|
35
|
+
const opacity = interpolate(frame, [0, 60], [0, 1], {
|
|
36
|
+
easing: Easing.bezier(0.16, 1, 0.3, 1),
|
|
37
|
+
extrapolateLeft: "clamp",
|
|
38
|
+
extrapolateRight: "clamp",
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Examples (copy-paste curves)
|
|
43
|
+
|
|
44
|
+
**1. Crisp UI entrance (strong ease-out, no overshoot)** — slows nicely into the rest value; similar to many system “deceleration” curves.
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
const enter = interpolate(frame, [0, 45], [0, 1], {
|
|
48
|
+
easing: Easing.bezier(0.16, 1, 0.3, 1),
|
|
49
|
+
extrapolateLeft: "clamp",
|
|
50
|
+
extrapolateRight: "clamp",
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**2. Editorial / slow fade (balanced ease-in-out)** — symmetric acceleration and deceleration over a hold-friendly move.
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
const progress = interpolate(frame, [0, 90], [0, 1], {
|
|
58
|
+
easing: Easing.bezier(0.45, 0, 0.55, 1),
|
|
59
|
+
extrapolateLeft: "clamp",
|
|
60
|
+
extrapolateRight: "clamp",
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**3. Playful overshoot (control point y > 1)** — a little past the target then settles; use sparingly for emphasis.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
const pop = interpolate(frame, [0, 30], [0, 1], {
|
|
68
|
+
easing: Easing.bezier(0.34, 1.56, 0.64, 1),
|
|
69
|
+
extrapolateLeft: "clamp",
|
|
70
|
+
extrapolateRight: "clamp",
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Preset easings (`Easing.in` / `Easing.out` / named curves)
|
|
75
|
+
|
|
76
|
+
Easing can be added to the `interpolate` function without a custom cubic:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { interpolate, Easing } from "remotion";
|
|
80
|
+
|
|
81
|
+
const value1 = interpolate(frame, [0, 100], [0, 1], {
|
|
82
|
+
easing: Easing.inOut(Easing.cubic),
|
|
83
|
+
extrapolateLeft: "clamp",
|
|
84
|
+
extrapolateRight: "clamp",
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The default easing is `Easing.linear`.
|
|
89
|
+
Convexities:
|
|
90
|
+
|
|
91
|
+
- `Easing.in` — starting slow and accelerating
|
|
92
|
+
- `Easing.out` — starting fast and slowing down
|
|
93
|
+
- `Easing.inOut`
|
|
94
|
+
|
|
95
|
+
Named curves (from most linear to most curved):
|
|
96
|
+
|
|
97
|
+
- `Easing.quad`
|
|
98
|
+
- `Easing.cubic` (good default when you do not need a custom cubic)
|
|
99
|
+
- `Easing.sin`
|
|
100
|
+
- `Easing.exp`
|
|
101
|
+
- `Easing.circle`
|
|
102
|
+
|
|
103
|
+
### Easing direction for enter/exit animations
|
|
104
|
+
|
|
105
|
+
Use `Easing.out` for enter animations (starts fast, decelerates into place) and `Easing.in` for exit animations (starts slow, accelerates away). This feels natural because elements arrive with momentum and leave with gravity. When you need a specific curve from design, prefer a single `Easing.bezier(...)` instead of stacking presets.
|
|
106
|
+
|
|
107
|
+
## Composing interpolations
|
|
108
|
+
|
|
109
|
+
When multiple properties share the same timing (e.g. a slide-in panel and a video shift), avoid duplicating the full interpolation for each property. Instead, create a single normalized progress value (0 to 1) and derive each property from it:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
const slideIn = interpolate(
|
|
113
|
+
frame,
|
|
114
|
+
[slideInStart, slideInStart + slideInDuration],
|
|
115
|
+
[0, 1],
|
|
116
|
+
{
|
|
117
|
+
easing: Easing.bezier(0.22, 1, 0.36, 1),
|
|
118
|
+
extrapolateLeft: "clamp",
|
|
119
|
+
extrapolateRight: "clamp",
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
const slideOut = interpolate(
|
|
123
|
+
frame,
|
|
124
|
+
[slideOutStart, slideOutStart + slideOutDuration],
|
|
125
|
+
[0, 1],
|
|
126
|
+
{ easing: Easing.in(Easing.cubic), extrapolateLeft: "clamp", extrapolateRight: "clamp" },
|
|
127
|
+
);
|
|
128
|
+
const progress = slideIn - slideOut;
|
|
129
|
+
|
|
130
|
+
// Derive multiple properties from the same progress
|
|
131
|
+
const overlayX = interpolate(progress, [0, 1], [100, 0]);
|
|
132
|
+
const videoX = interpolate(progress, [0, 1], [0, -20]);
|
|
133
|
+
const opacity = interpolate(progress, [0, 1], [0, 1]);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The key idea: separate **timing** (when and how fast) from **mapping** (what values to animate between).
|