@editframe/create 0.44.0 → 0.45.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/dist/index.js +16 -28
- package/dist/index.js.map +1 -1
- package/dist/skills/editframe-brand-video-generator/README.md +155 -0
- package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
- package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
- package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
- package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
- package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
- package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
- package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
- package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
- package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
- package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
- package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
- package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
- package/dist/skills/editframe-composition/SKILL.md +169 -0
- package/dist/skills/editframe-composition/references/audio.md +483 -0
- package/dist/skills/editframe-composition/references/captions.md +844 -0
- package/dist/skills/editframe-composition/references/composition-model.md +73 -0
- package/dist/skills/editframe-composition/references/configuration.md +403 -0
- package/dist/skills/editframe-composition/references/css-parts.md +105 -0
- package/dist/skills/editframe-composition/references/css-variables.md +640 -0
- package/dist/skills/editframe-composition/references/entry-points.md +810 -0
- package/dist/skills/editframe-composition/references/events.md +499 -0
- package/dist/skills/editframe-composition/references/getting-started.md +259 -0
- package/dist/skills/editframe-composition/references/hooks.md +234 -0
- package/dist/skills/editframe-composition/references/image.md +241 -0
- package/dist/skills/editframe-composition/references/r3f.md +580 -0
- package/dist/skills/editframe-composition/references/render-api.md +484 -0
- package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
- package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
- package/dist/skills/editframe-composition/references/scripting.md +606 -0
- package/dist/skills/editframe-composition/references/sequencing.md +116 -0
- package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
- package/dist/skills/editframe-composition/references/surface.md +329 -0
- package/dist/skills/editframe-composition/references/text.md +627 -0
- package/dist/skills/editframe-composition/references/time-model.md +99 -0
- package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
- package/dist/skills/editframe-composition/references/timegroup.md +457 -0
- package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
- package/dist/skills/editframe-composition/references/transcription.md +47 -0
- package/dist/skills/editframe-composition/references/transitions.md +608 -0
- package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
- package/dist/skills/editframe-composition/references/video.md +506 -0
- package/dist/skills/editframe-composition/references/waveform.md +327 -0
- package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
- package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
- package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
- package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
- package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
- package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
- package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
- package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
- package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
- package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
- package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
- package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
- package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
- package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
- package/dist/skills/editframe-editor-gui/references/play.md +370 -0
- package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
- package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
- package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
- package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
- package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
- package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
- package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
- package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
- package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
- package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
- package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
- package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
- package/dist/skills/editframe-motion-design/SKILL.md +101 -0
- package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
- package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
- package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
- package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
- package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
- package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
- package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
- package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
- package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
- package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
- package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
- package/dist/skills/editframe-webhooks/SKILL.md +126 -0
- package/dist/skills/editframe-webhooks/references/events.md +382 -0
- package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
- package/dist/skills/editframe-webhooks/references/security.md +418 -0
- package/dist/skills/editframe-webhooks/references/testing.md +409 -0
- package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
- package/dist/templates/html/AGENTS.md +13 -0
- package/dist/templates/react/AGENTS.md +13 -0
- package/dist/utils.js +15 -16
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/tsdown.config.ts +4 -0
- package/dist/detectAgent.js +0 -89
- package/dist/detectAgent.js.map +0 -1
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Getting Started
|
|
3
|
+
description: Build your first HTML video composition with Editframe Elements — install the package, place elements, and render to MP4.
|
|
4
|
+
type: tutorial
|
|
5
|
+
nav:
|
|
6
|
+
parent: "Quick Start"
|
|
7
|
+
priority: 1
|
|
8
|
+
track: "getting-started"
|
|
9
|
+
track_step: 1
|
|
10
|
+
track_title: "Your First Composition"
|
|
11
|
+
next_steps: ["video"]
|
|
12
|
+
react:
|
|
13
|
+
generate: true
|
|
14
|
+
componentName: "Getting Started"
|
|
15
|
+
importPath: "@editframe/react"
|
|
16
|
+
nav:
|
|
17
|
+
parent: "Quick Start"
|
|
18
|
+
priority: 0
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# Getting Started
|
|
22
|
+
|
|
23
|
+
<!-- html-only -->
|
|
24
|
+
Build your first video composition with Editframe Elements.
|
|
25
|
+
|
|
26
|
+
> **Note:** Need a project? Run `npm create @editframe -- html -d my-project -y` — see the `editframe-create` skill.
|
|
27
|
+
<!-- /html-only -->
|
|
28
|
+
<!-- react-only -->
|
|
29
|
+
Build your first video composition with React.
|
|
30
|
+
|
|
31
|
+
> **Note:** Need a project? Run `npm create @editframe -- react -d my-project -y` — see the `editframe-create` skill.
|
|
32
|
+
<!-- /react-only -->
|
|
33
|
+
|
|
34
|
+
## Composition Structure
|
|
35
|
+
|
|
36
|
+
<!-- html-only -->
|
|
37
|
+
Every composition starts with an `ef-timegroup` root:
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<ef-timegroup mode="sequence" class="w-[1920px] h-[1080px] bg-black">
|
|
41
|
+
<!-- scenes go here -->
|
|
42
|
+
</ef-timegroup>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The `mode` controls how child elements are timed.
|
|
46
|
+
|
|
47
|
+
### Step 1: Start with Motion
|
|
48
|
+
|
|
49
|
+
The first thing a viewer sees should move. Add an animated title — every word slides up in sequence using `split="word"` and `--ef-word-index`:
|
|
50
|
+
|
|
51
|
+
```html live
|
|
52
|
+
<ef-timegroup mode="fixed" duration="3s" class="w-[720px] h-[400px] bg-black flex items-center justify-center">
|
|
53
|
+
<ef-text
|
|
54
|
+
split="word"
|
|
55
|
+
class="text-white text-5xl font-bold"
|
|
56
|
+
style="animation: 0.6s slide-up both; animation-delay: calc(var(--ef-word-index) * 100ms)"
|
|
57
|
+
>Hello Editframe</ef-text>
|
|
58
|
+
</ef-timegroup>
|
|
59
|
+
<style>
|
|
60
|
+
@keyframes slide-up {
|
|
61
|
+
from { transform: translateY(30px); opacity: 0; }
|
|
62
|
+
to { transform: translateY(0); opacity: 1; }
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`split="word"` breaks the text into individual word elements. Each gets a `--ef-word-index` CSS variable so you can stagger delays. The animation runs relative to the scene timeline — it's fully scrubbable.
|
|
68
|
+
|
|
69
|
+
### Step 2: Add a Video Background
|
|
70
|
+
|
|
71
|
+
Place the title inside a `contain` timegroup with a video beneath it. The timegroup holds both layers simultaneously:
|
|
72
|
+
|
|
73
|
+
```html live
|
|
74
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[400px] bg-black">
|
|
75
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="absolute inset-0 size-full object-cover"></ef-video>
|
|
76
|
+
<div class="absolute inset-0 bg-black/50"></div>
|
|
77
|
+
<ef-text
|
|
78
|
+
split="word"
|
|
79
|
+
class="absolute top-8 left-8 text-white text-4xl font-bold"
|
|
80
|
+
style="animation: 0.6s slide-up both; animation-delay: calc(var(--ef-word-index) * 100ms)"
|
|
81
|
+
>Hello Editframe</ef-text>
|
|
82
|
+
</ef-timegroup>
|
|
83
|
+
<style>
|
|
84
|
+
@keyframes slide-up {
|
|
85
|
+
from { transform: translateY(30px); opacity: 0; }
|
|
86
|
+
to { transform: translateY(0); opacity: 1; }
|
|
87
|
+
}
|
|
88
|
+
</style>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Step 3: Chain Scenes with a Transition
|
|
92
|
+
|
|
93
|
+
Use `mode="sequence"` with `overlap` to crossfade between scenes. Each scene fades out while the next fades in:
|
|
94
|
+
|
|
95
|
+
```html live
|
|
96
|
+
<ef-timegroup mode="sequence" overlap="1s" class="w-[720px] h-[400px] bg-black">
|
|
97
|
+
<ef-timegroup mode="contain" class="absolute w-full h-full" style="animation: 1s fade-out var(--ef-transition-out-start) both">
|
|
98
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" sourcein="0s" sourceout="4s" class="absolute inset-0 size-full object-cover"></ef-video>
|
|
99
|
+
<ef-text
|
|
100
|
+
split="word"
|
|
101
|
+
class="absolute top-8 left-8 text-white text-4xl font-bold"
|
|
102
|
+
style="animation: 0.6s slide-up both; animation-delay: calc(var(--ef-word-index) * 100ms)"
|
|
103
|
+
>Scene One</ef-text>
|
|
104
|
+
</ef-timegroup>
|
|
105
|
+
<ef-timegroup mode="contain" class="absolute w-full h-full" style="animation: 1s fade-in both">
|
|
106
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" sourcein="5s" sourceout="9s" class="absolute inset-0 size-full object-cover"></ef-video>
|
|
107
|
+
<ef-text
|
|
108
|
+
split="word"
|
|
109
|
+
class="absolute top-8 left-8 text-white text-4xl font-bold"
|
|
110
|
+
style="animation: 0.6s slide-up 0.3s both; animation-delay: calc(0.3s + var(--ef-word-index) * 100ms)"
|
|
111
|
+
>Scene Two</ef-text>
|
|
112
|
+
</ef-timegroup>
|
|
113
|
+
</ef-timegroup>
|
|
114
|
+
<style>
|
|
115
|
+
@keyframes slide-up {
|
|
116
|
+
from { transform: translateY(30px); opacity: 0; }
|
|
117
|
+
to { transform: translateY(0); opacity: 1; }
|
|
118
|
+
}
|
|
119
|
+
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
120
|
+
@keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }
|
|
121
|
+
</style>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`--ef-transition-out-start` is a CSS variable Editframe sets automatically — it's the point in time when the overlap begins, so the fade-out starts exactly at the transition point.
|
|
125
|
+
|
|
126
|
+
<!-- /html-only -->
|
|
127
|
+
<!-- react-only -->
|
|
128
|
+
## Motion-First Composition
|
|
129
|
+
|
|
130
|
+
The first thing a viewer sees should move. This example animates a title word-by-word using `split="word"` and `--ef-word-index`:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { Timegroup, Text } from "@editframe/react";
|
|
134
|
+
|
|
135
|
+
export const Video = () => {
|
|
136
|
+
return (
|
|
137
|
+
<Timegroup mode="fixed" duration="3s" className="w-[1920px] h-[1080px] bg-black flex items-center justify-center">
|
|
138
|
+
<Text
|
|
139
|
+
split="word"
|
|
140
|
+
className="text-white text-7xl font-bold"
|
|
141
|
+
style={{
|
|
142
|
+
animation: "0.6s slide-up both",
|
|
143
|
+
animationDelay: "calc(var(--ef-word-index) * 100ms)"
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
Hello Editframe
|
|
147
|
+
</Text>
|
|
148
|
+
</Timegroup>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```css
|
|
154
|
+
@keyframes slide-up {
|
|
155
|
+
from { transform: translateY(30px); opacity: 0; }
|
|
156
|
+
to { transform: translateY(0); opacity: 1; }
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Wrap it with `TimelineRoot` in `src/main.tsx`:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import React from "react";
|
|
164
|
+
import ReactDOM from "react-dom/client";
|
|
165
|
+
import { TimelineRoot } from "@editframe/react";
|
|
166
|
+
import { Video } from "./Video";
|
|
167
|
+
import "@editframe/elements/styles.css";
|
|
168
|
+
|
|
169
|
+
const root = document.getElementById("root");
|
|
170
|
+
if (!root) throw new Error("Root element not found");
|
|
171
|
+
|
|
172
|
+
ReactDOM.createRoot(root).render(
|
|
173
|
+
<TimelineRoot id="root" component={Video} />
|
|
174
|
+
);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Important**: `TimelineRoot` is required for rendering. See [timeline-root.md](references/timeline-root.md).
|
|
178
|
+
|
|
179
|
+
## Add a Video Background
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
import { Timegroup, Video, Text } from "@editframe/react";
|
|
183
|
+
|
|
184
|
+
export const MyVideo = () => (
|
|
185
|
+
<Timegroup mode="contain" className="w-[1920px] h-[1080px] bg-black">
|
|
186
|
+
<Video src="/assets/background.mp4" className="absolute inset-0 size-full object-cover" />
|
|
187
|
+
<div className="absolute inset-0 bg-black/50" />
|
|
188
|
+
<Text
|
|
189
|
+
split="word"
|
|
190
|
+
className="absolute top-16 left-16 text-white text-6xl font-bold"
|
|
191
|
+
style={{
|
|
192
|
+
animation: "0.6s slide-up both",
|
|
193
|
+
animationDelay: "calc(var(--ef-word-index) * 100ms)"
|
|
194
|
+
}}
|
|
195
|
+
>
|
|
196
|
+
Opening Title
|
|
197
|
+
</Text>
|
|
198
|
+
</Timegroup>
|
|
199
|
+
);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Chain Scenes with a Transition
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { Timegroup, Video, Text, Audio } from "@editframe/react";
|
|
206
|
+
|
|
207
|
+
export const VideoComposition = () => (
|
|
208
|
+
<Timegroup mode="sequence" overlap="1s" className="w-[1920px] h-[1080px]">
|
|
209
|
+
<Timegroup
|
|
210
|
+
mode="contain"
|
|
211
|
+
className="absolute w-full h-full"
|
|
212
|
+
style={{ animation: "1s fade-out var(--ef-transition-out-start) both" }}
|
|
213
|
+
>
|
|
214
|
+
<Video src="/assets/intro.mp4" className="size-full object-cover" />
|
|
215
|
+
<Text
|
|
216
|
+
split="word"
|
|
217
|
+
className="absolute top-16 left-16 text-white text-5xl font-bold"
|
|
218
|
+
style={{ animation: "0.6s slide-up both", animationDelay: "calc(var(--ef-word-index) * 100ms)" }}
|
|
219
|
+
>
|
|
220
|
+
Scene One
|
|
221
|
+
</Text>
|
|
222
|
+
</Timegroup>
|
|
223
|
+
<Timegroup
|
|
224
|
+
mode="contain"
|
|
225
|
+
className="absolute w-full h-full"
|
|
226
|
+
style={{ animation: "1s fade-in both" }}
|
|
227
|
+
>
|
|
228
|
+
<Video src="/assets/main.mp4" className="size-full object-cover" />
|
|
229
|
+
<Audio src="/assets/music.mp3" volume={0.3} />
|
|
230
|
+
</Timegroup>
|
|
231
|
+
</Timegroup>
|
|
232
|
+
);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```css
|
|
236
|
+
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
237
|
+
@keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }
|
|
238
|
+
```
|
|
239
|
+
<!-- /react-only -->
|
|
240
|
+
|
|
241
|
+
## Assets
|
|
242
|
+
|
|
243
|
+
Place media files in `src/assets/` and reference with `/assets/filename`:
|
|
244
|
+
|
|
245
|
+
<!-- html-only -->
|
|
246
|
+
```html
|
|
247
|
+
<ef-video src="/assets/video.mp4"></ef-video>
|
|
248
|
+
<ef-audio src="/assets/music.mp3"></ef-audio>
|
|
249
|
+
<ef-image src="/assets/logo.png"></ef-image>
|
|
250
|
+
```
|
|
251
|
+
<!-- /html-only -->
|
|
252
|
+
|
|
253
|
+
## Render to Video
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
npx editframe render -o output.mp4
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
See the `editframe-cli` skill for full render options.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Hooks
|
|
3
|
+
description: React hooks for reading current playback time, pan/zoom transform, and media loading state from within composition components.
|
|
4
|
+
type: reference
|
|
5
|
+
nav:
|
|
6
|
+
parent: "React"
|
|
7
|
+
priority: 11
|
|
8
|
+
related: ["timeline-root", "timegroup"]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Hooks
|
|
12
|
+
|
|
13
|
+
React hooks for accessing timing information and transforms.
|
|
14
|
+
|
|
15
|
+
## useTimingInfo
|
|
16
|
+
|
|
17
|
+
Access timing information for the current element.
|
|
18
|
+
|
|
19
|
+
**Important**: This hook requires `TimelineRoot` to work correctly in render clones. See [timeline-root.md](references/timeline-root.md).
|
|
20
|
+
|
|
21
|
+
### Import
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { useTimingInfo } from "@editframe/react";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Returns
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
{
|
|
31
|
+
ref: RefObject<HTMLElement>; // Attach to your component
|
|
32
|
+
ownCurrentTimeMs: number; // Current time within this element
|
|
33
|
+
durationMs: number; // Total duration of element
|
|
34
|
+
percentComplete: number; // Progress (0-1)
|
|
35
|
+
isActive: boolean; // Is currently playing
|
|
36
|
+
startTimeMs: number; // Start time in parent
|
|
37
|
+
endTimeMs: number; // End time in parent
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Basic Usage
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { Timegroup, useTimingInfo } from "@editframe/react";
|
|
45
|
+
|
|
46
|
+
const AnimatedScene = () => {
|
|
47
|
+
const { ref, percentComplete, ownCurrentTimeMs } = useTimingInfo();
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Timegroup ref={ref} mode="fixed" duration="5s" className="absolute w-full h-full">
|
|
51
|
+
<div style={{ opacity: percentComplete }}>
|
|
52
|
+
Fading in... {(ownCurrentTimeMs / 1000).toFixed(2)}s
|
|
53
|
+
</div>
|
|
54
|
+
</Timegroup>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Fade In/Out
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
const FadeInOut = ({ children }: { children: React.ReactNode }) => {
|
|
63
|
+
const { ref, percentComplete } = useTimingInfo();
|
|
64
|
+
|
|
65
|
+
// Fade in first half, fade out second half
|
|
66
|
+
const opacity = percentComplete < 0.5
|
|
67
|
+
? percentComplete * 2
|
|
68
|
+
: (1 - percentComplete) * 2;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Timegroup ref={ref} mode="fixed" duration="5s" className="absolute w-full h-full">
|
|
72
|
+
<div style={{ opacity }}>
|
|
73
|
+
{children}
|
|
74
|
+
</div>
|
|
75
|
+
</Timegroup>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Scale Animation
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
const ScaleIn = ({ children }: { children: React.ReactNode }) => {
|
|
84
|
+
const { ref, percentComplete } = useTimingInfo();
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Timegroup ref={ref} mode="fixed" duration="3s" className="absolute w-full h-full flex items-center justify-center">
|
|
88
|
+
<div style={{ transform: `scale(${percentComplete})` }}>
|
|
89
|
+
{children}
|
|
90
|
+
</div>
|
|
91
|
+
</Timegroup>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Progress Bar
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
const SceneWithProgress = () => {
|
|
100
|
+
const { ref, percentComplete, ownCurrentTimeMs, durationMs } = useTimingInfo();
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<Timegroup ref={ref} mode="fixed" duration="10s" className="absolute w-full h-full bg-black flex flex-col items-center justify-center">
|
|
104
|
+
<h1 className="text-white text-4xl mb-4">Scene Content</h1>
|
|
105
|
+
|
|
106
|
+
<div className="w-96 bg-gray-700 h-2 rounded">
|
|
107
|
+
<div
|
|
108
|
+
className="bg-blue-500 h-full rounded transition-all"
|
|
109
|
+
style={{ width: `${percentComplete * 100}%` }}
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<p className="text-white mt-2">
|
|
114
|
+
{(ownCurrentTimeMs / 1000).toFixed(1)}s / {(durationMs / 1000).toFixed(1)}s
|
|
115
|
+
</p>
|
|
116
|
+
</Timegroup>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Conditional Rendering
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
const TimedReveal = () => {
|
|
125
|
+
const { ref, ownCurrentTimeMs } = useTimingInfo();
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<Timegroup ref={ref} mode="fixed" duration="10s" className="absolute w-full h-full">
|
|
129
|
+
{ownCurrentTimeMs > 2000 && (
|
|
130
|
+
<Text duration="8s" className="text-white text-4xl">
|
|
131
|
+
Appears after 2 seconds
|
|
132
|
+
</Text>
|
|
133
|
+
)}
|
|
134
|
+
|
|
135
|
+
{ownCurrentTimeMs > 5000 && (
|
|
136
|
+
<Image src="/assets/logo.png" className="absolute top-8 right-8 w-32" />
|
|
137
|
+
)}
|
|
138
|
+
</Timegroup>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Complex Animation
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
const ComplexAnimation = ({ children }: { children: React.ReactNode }) => {
|
|
147
|
+
const { ref, percentComplete } = useTimingInfo();
|
|
148
|
+
|
|
149
|
+
// Different animations per stage
|
|
150
|
+
let transform = '';
|
|
151
|
+
let opacity = 1;
|
|
152
|
+
|
|
153
|
+
if (percentComplete < 0.25) {
|
|
154
|
+
// Slide in from left
|
|
155
|
+
const progress = percentComplete * 4;
|
|
156
|
+
transform = `translateX(${-100 + progress * 100}%)`;
|
|
157
|
+
} else if (percentComplete < 0.75) {
|
|
158
|
+
// Stay centered
|
|
159
|
+
transform = 'translateX(0)';
|
|
160
|
+
} else {
|
|
161
|
+
// Slide out to right
|
|
162
|
+
const progress = (percentComplete - 0.75) * 4;
|
|
163
|
+
transform = `translateX(${progress * 100}%)`;
|
|
164
|
+
opacity = 1 - progress;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<Timegroup ref={ref} mode="fixed" duration="8s" className="absolute w-full h-full flex items-center justify-center">
|
|
169
|
+
<div style={{ transform, opacity }}>
|
|
170
|
+
{children}
|
|
171
|
+
</div>
|
|
172
|
+
</Timegroup>
|
|
173
|
+
);
|
|
174
|
+
};
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## usePanZoomTransform
|
|
178
|
+
|
|
179
|
+
Access pan/zoom transform values for synchronized UI elements.
|
|
180
|
+
|
|
181
|
+
### Import
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
import { usePanZoomTransform } from "@editframe/react";
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Returns
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
{
|
|
191
|
+
x: number; // Pan X offset
|
|
192
|
+
y: number; // Pan Y offset
|
|
193
|
+
scale: number; // Zoom scale
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Basic Usage
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
import { PanZoom, usePanZoomTransform } from "@editframe/react";
|
|
201
|
+
|
|
202
|
+
const SyncedOverlay = () => {
|
|
203
|
+
const { x, y, scale } = usePanZoomTransform();
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<div
|
|
207
|
+
className="absolute top-0 left-0 pointer-events-none"
|
|
208
|
+
style={{
|
|
209
|
+
transform: `translate(${x}px, ${y}px) scale(${scale})`,
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
212
|
+
<div className="text-white text-2xl">
|
|
213
|
+
Follows pan/zoom
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
};
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Display Transform Info
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
const TransformInfo = () => {
|
|
224
|
+
const { x, y, scale } = usePanZoomTransform();
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<div className="absolute top-4 right-4 bg-black/80 text-white p-2 rounded text-sm font-mono">
|
|
228
|
+
<div>X: {x.toFixed(0)}px</div>
|
|
229
|
+
<div>Y: {y.toFixed(0)}px</div>
|
|
230
|
+
<div>Scale: {scale.toFixed(2)}x</div>
|
|
231
|
+
</div>
|
|
232
|
+
);
|
|
233
|
+
};
|
|
234
|
+
```
|