reszta poprawek
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
---
|
||||
name: remotion-interactivity
|
||||
description: Structure Remotion markup for interactivity
|
||||
metadata:
|
||||
tags: remotion, interactivity, studio, visual mode
|
||||
---
|
||||
|
||||
By writing Remotion markup in a specific way, the Remotion Studio is able to recognize the structure of the code and makes it interactive:
|
||||
|
||||
- Allowing items to be selected by clicking on them
|
||||
- Allowing drag+drop, resizing and rotation
|
||||
- Editing the CSS styles
|
||||
- Making keyframes and easing values editable
|
||||
|
||||
If the markup is too complex for the Studio to make it interactive, then the values become grayed out.
|
||||
|
||||
## Make an HTML element interactive using `Interactive`
|
||||
|
||||
Every HTML and SVG element such as `<div>` can be turned interactive using `Interactive`:
|
||||
|
||||
```tsx title="Interactive elements"
|
||||
<Interactive.Div name="Greeting card" style={{fontSize: 80, padding: 24}}>
|
||||
Hello
|
||||
</Interactive.Div>
|
||||
```
|
||||
|
||||
This allows styles and keyframes to be set in the Studio. Be sensible, if a component has many elements, the timeline might get messy.
|
||||
|
||||
## Give interactive elements a descriptive name
|
||||
|
||||
Add a `name` prop to elements to make them easily identifyable.
|
||||
|
||||
```tsx title="Interactive names"
|
||||
<>
|
||||
<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" />
|
||||
<Sequence name="Title">
|
||||
Launch day
|
||||
</Sequence>
|
||||
</>
|
||||
```
|
||||
|
||||
## Keep all CSS styles inline
|
||||
|
||||
The best way is to just pass a plain object to `style` - no referring to constants, no object spreading, no math.
|
||||
|
||||
```tsx title="Interactive example"
|
||||
<Interactive.Div
|
||||
style={{
|
||||
fontSize: 80,
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
Hello World!
|
||||
</Interactive.Div>
|
||||
```
|
||||
|
||||
```tsx title="❌ Bad for interactivity"
|
||||
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>
|
||||
```
|
||||
|
||||
## Animate using `interpolate()`
|
||||
|
||||
Write animations as inline `interpolate()` calls on the property that changes.
|
||||
The output range, easing, extrapolation and `output` property should use hardcoded values.
|
||||
|
||||
The input range may additionally use `durationInFrames`, `fps`, `width` and `height` destructured directly from `useVideoConfig()`. Bare identifiers such as `durationInFrames`, multiplication with a number such as `2 * fps` or `fps * 2`, and subtraction of a number such as `durationInFrames - 1` are supported.
|
||||
|
||||
```tsx title="Inline values"
|
||||
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'
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
```tsx title="❌ Bad interactivity"
|
||||
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.
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
## Use `scale`, `translate`, `rotate` CSS properties
|
||||
|
||||
Avoid the `transform` CSS property.
|
||||
If possible, use `scale`, `rotate` and `translate` instead because only they are interactively editable.
|
||||
|
||||
## Keep composition metadata inline
|
||||
|
||||
When scaffolding a composition, keep `width`, `height`, `fps`, `durationInFrames` and `defaultProps` inline and make no type assertions.
|
||||
|
||||
The Props editor can save visual edits back to your code when `defaultProps` is an inline object literal on `<Composition>` or `<Still>`.
|
||||
|
||||
```tsx
|
||||
// 👍 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'}}
|
||||
/>
|
||||
```
|
||||
|
||||
```tsx title="Negative examples"
|
||||
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
|
||||
/>
|
||||
```
|
||||
|
||||
Use only `calculateMetadata()` for the part of the metadata that is dynamic.
|
||||
|
||||
## Effects should be inline too
|
||||
|
||||
The effects array should not be computed.
|
||||
The same rules for setting keyframes as `interpolate()` apply too here: All values should also be hardcoded: Input range, output range, easing, extrapolation, `output` property.
|
||||
|
||||
```tsx title="Effects"
|
||||
// 👍 Parameters are inline and the array shape is stable
|
||||
<CanvasImage
|
||||
src={src}
|
||||
width={1280}
|
||||
height={720}
|
||||
effects={[
|
||||
radialProgressiveBlur({
|
||||
center: [0.5, 0.5],
|
||||
width: 1.2,
|
||||
height: 0.8,
|
||||
start: 0.2,
|
||||
disabled: true,
|
||||
rotation: interpolate(frame, [0, 120], [0, 180]),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
|
||||
const center = [0.5, 0.5] as const;
|
||||
const rotation = frame * 1.5;
|
||||
|
||||
<CanvasImage
|
||||
src={src}
|
||||
width={1280}
|
||||
height={720}
|
||||
// ❌ Conditional effect is not animateable
|
||||
effects={enabled ? [
|
||||
radialProgressiveBlur({
|
||||
// ❌ Not inline
|
||||
center,
|
||||
rotation,
|
||||
}),
|
||||
] : []}
|
||||
/>
|
||||
```
|
||||
|
||||
Render separate elements if one version should have effects and another should not.
|
||||
|
||||
## Making your own component interactive
|
||||
|
||||
To make a custom userland component interactive, use:
|
||||
[Make a component interactive](https://www.remotion.dev/docs/studio/make-component-interactive.md)
|
||||
|
||||
## Video editing
|
||||
|
||||
If a Remotion component mainly consists of video and audio clips, see [Video editing](../remotion-markup/video-editing.md) for best practices on how to structure Remotion markup so the clips are interactively editable in the timeline.
|
||||
Reference in New Issue
Block a user