@twick/visualizer 0.0.1 → 0.14.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.
Files changed (61) hide show
  1. package/.turbo/turbo-build.log +19 -0
  2. package/.turbo/turbo-docs.log +7 -0
  3. package/LICENSE +197 -0
  4. package/README.md +1 -1
  5. package/dist/mp4.wasm +0 -0
  6. package/dist/project.css +1 -0
  7. package/dist/project.js +145 -0
  8. package/docs/.nojekyll +1 -0
  9. package/docs/README.md +13 -0
  10. package/docs/interfaces/Animation.md +47 -0
  11. package/docs/interfaces/Element.md +47 -0
  12. package/docs/interfaces/FrameEffectPlugin.md +47 -0
  13. package/docs/interfaces/TextEffect.md +47 -0
  14. package/docs/modules.md +535 -0
  15. package/package.json +34 -31
  16. package/src/animations/blur.tsx +60 -0
  17. package/src/animations/breathe.tsx +60 -0
  18. package/src/animations/fade.tsx +60 -0
  19. package/src/animations/photo-rise.tsx +66 -0
  20. package/src/animations/photo-zoom.tsx +73 -0
  21. package/src/animations/rise.tsx +118 -0
  22. package/src/animations/succession.tsx +77 -0
  23. package/src/components/frame-effects.tsx +2 -4
  24. package/src/components/track.tsx +232 -0
  25. package/src/controllers/animation.controller.ts +39 -0
  26. package/src/controllers/element.controller.ts +43 -0
  27. package/src/controllers/frame-effect.controller.tsx +30 -0
  28. package/src/controllers/text-effect.controller.ts +33 -0
  29. package/src/elements/audio.element.tsx +17 -0
  30. package/src/elements/caption.element.tsx +87 -0
  31. package/src/elements/circle.element.tsx +20 -0
  32. package/src/elements/icon.element.tsx +20 -0
  33. package/src/elements/image.element.tsx +55 -0
  34. package/src/elements/rect.element.tsx +22 -0
  35. package/src/elements/scene.element.tsx +29 -0
  36. package/src/elements/text.element.tsx +28 -0
  37. package/src/elements/video.element.tsx +56 -0
  38. package/src/frame-effects/circle.frame.tsx +103 -0
  39. package/src/frame-effects/rect.frame.tsx +103 -0
  40. package/src/helpers/caption.utils.ts +4 -14
  41. package/src/helpers/constants.ts +1 -1
  42. package/src/helpers/element.utils.ts +222 -68
  43. package/src/helpers/filters.ts +1 -1
  44. package/src/helpers/log.utils.ts +0 -3
  45. package/src/helpers/timing.utils.ts +2 -21
  46. package/src/helpers/types.ts +103 -8
  47. package/src/helpers/utils.ts +20 -0
  48. package/src/index.ts +4 -2
  49. package/src/live.tsx +1 -1
  50. package/src/project.ts +1 -1
  51. package/src/sample.ts +16 -218
  52. package/src/text-effects/elastic.tsx +39 -0
  53. package/src/text-effects/erase.tsx +58 -0
  54. package/src/text-effects/stream-word.tsx +60 -0
  55. package/src/text-effects/typewriter.tsx +59 -0
  56. package/src/visualizer.tsx +27 -27
  57. package/tsconfig.json +3 -2
  58. package/vite.config.ts +1 -1
  59. package/src/components/animation.tsx +0 -7
  60. package/src/components/element.tsx +0 -344
  61. package/src/components/timeline.tsx +0 -225
@@ -0,0 +1,59 @@
1
+ import { waitFor } from "@twick/core";
2
+ import { TextEffectParams } from "../helpers/types";
3
+
4
+ /**
5
+ * TypewriterEffect animates text appearing one character at a time,
6
+ * mimicking the behavior of a classic typewriter.
7
+ *
8
+ * Behavior:
9
+ * - Optionally waits for a delay before starting.
10
+ * - Clears the text initially and preserves the element's original size.
11
+ * - Reveals one character at a time at a consistent interval.
12
+ *
13
+ * @param elementRef - Reference to the text element to animate.
14
+ * @param duration - Total duration of the animation.
15
+ * @param delay - Optional delay before starting.
16
+ * @param bufferTime - Time reserved at the end of animation (default: 0.1).
17
+ */
18
+ export const TypewriterEffect = {
19
+ name: "typewriter",
20
+
21
+ /**
22
+ * Generator function controlling the character-by-character typing animation.
23
+ */
24
+ *run({
25
+ elementRef,
26
+ duration,
27
+ delay,
28
+ bufferTime = 0.1,
29
+ }: TextEffectParams) {
30
+ // Retrieve the full text content
31
+ const fullText = elementRef().text();
32
+
33
+ // Store the element's size to prevent resizing during animation
34
+ const size = elementRef().size();
35
+
36
+ // Clear the text and set fixed size
37
+ elementRef().setText("");
38
+ elementRef().size(size);
39
+
40
+ // Align text to the left for consistent typing effect
41
+ elementRef().textAlign("left");
42
+
43
+ // Wait for an optional initial delay
44
+ if (delay) {
45
+ yield* waitFor(delay);
46
+ }
47
+
48
+ let timeInterval =(duration - bufferTime) / fullText.length;
49
+
50
+ // Wait briefly before starting typing
51
+ yield* waitFor(timeInterval);
52
+
53
+ // Reveal each character one by one
54
+ for (let i = 0; i < fullText.length; i++) {
55
+ yield* waitFor(timeInterval);
56
+ elementRef().setText(fullText.substring(0, i + 1));
57
+ }
58
+ },
59
+ };
@@ -4,19 +4,19 @@
4
4
  */
5
5
 
6
6
  import "./global.css";
7
- import { Rect, makeScene2D, View2D } from "@revideo/2d";
8
- import { all, useScene } from "@revideo/core";
7
+ import { Rect, makeScene2D, View2D } from "@twick/2d";
8
+ import { all, useScene } from "@twick/core";
9
9
 
10
- import { DEFAULT_BACKGROUND_COLOR, TIMELINE_TYPES } from "./helpers/constants";
10
+ import { DEFAULT_BACKGROUND_COLOR, TRACK_TYPES } from "./helpers/constants";
11
11
  import { VideoInput } from "./helpers/types";
12
12
  import { logger } from "./helpers/log.utils";
13
13
  import {
14
- makeAudioTimeline,
15
- makeCaptionTimeline,
16
- makeElementTimeline,
17
- makeSceneTimeline,
18
- makeVideoTimeline,
19
- } from "./components/timeline";
14
+ makeAudioTrack,
15
+ makeCaptionTrack,
16
+ makeElementTrack,
17
+ makeSceneTrack,
18
+ makeVideoTrack,
19
+ } from "./components/track";
20
20
 
21
21
  /**
22
22
  * Creates and configures the main scene for video visualization
@@ -29,7 +29,7 @@ export const scene = makeScene2D("scene", function* (view: View2D) {
29
29
  const input = useScene().variables.get("input", null)() as VideoInput | null;
30
30
 
31
31
  if (input) {
32
- logger("Scene updated");
32
+ logger("Scene updated", input);
33
33
 
34
34
  // Add background rectangle with specified or default color
35
35
  yield view.add(
@@ -39,39 +39,39 @@ export const scene = makeScene2D("scene", function* (view: View2D) {
39
39
  />
40
40
  );
41
41
 
42
- // Process timeline elements if present
43
- if (input.timeline) {
42
+ // Process track elements if present
43
+ if (input.tracks) {
44
44
  const movie = [];
45
45
  let index = 1;
46
46
 
47
- // Iterate through each timeline element and create appropriate visualization
48
- for (const timeline of input.timeline) {
49
- switch (timeline.type) {
50
- case TIMELINE_TYPES.VIDEO:
51
- movie.push(makeVideoTimeline({ view, timeline }));
47
+ // Iterate through each track element and create appropriate visualization
48
+ for (const track of input.tracks) {
49
+ switch (track.type) {
50
+ case TRACK_TYPES.VIDEO:
51
+ movie.push(makeVideoTrack({ view, track }));
52
52
  break;
53
- case TIMELINE_TYPES.AUDIO:
54
- movie.push(makeAudioTimeline({ view, timeline }));
53
+ case TRACK_TYPES.AUDIO:
54
+ movie.push(makeAudioTrack({ view, track }));
55
55
  break;
56
- case TIMELINE_TYPES.CAPTION:
56
+ case TRACK_TYPES.CAPTION:
57
57
  movie.push(
58
- makeCaptionTimeline({
58
+ makeCaptionTrack({
59
59
  view,
60
- timeline,
60
+ track,
61
61
  })
62
62
  );
63
63
  break;
64
- case TIMELINE_TYPES.SCENE:
65
- movie.push(makeSceneTimeline({ view, timeline }));
64
+ case TRACK_TYPES.SCENE:
65
+ movie.push(makeSceneTrack({ view, track }));
66
66
  break;
67
- case TIMELINE_TYPES.ELEMENT:
68
- movie.push(makeElementTimeline({ view, timeline }));
67
+ case TRACK_TYPES.ELEMENT:
68
+ movie.push(makeElementTrack({ view, track }));
69
69
  break;
70
70
  }
71
71
  index++;
72
72
  }
73
73
 
74
- // Execute all timeline animations in parallel
74
+ // Execute all track animations in parallel
75
75
  yield* all(...movie);
76
76
  }
77
77
  }
package/tsconfig.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
- "extends": "@revideo/2d/tsconfig.project.json",
2
+ "extends": "@twick/2d/tsconfig.project.json",
3
3
  "include": ["src"],
4
4
  "compilerOptions": {
5
5
  "noEmit": false,
6
6
  "outDir": "dist",
7
7
  "module": "CommonJS",
8
- "skipLibCheck": true
8
+ "skipLibCheck": true,
9
+ "jsx": "react-jsx"
9
10
  }
10
11
  }
package/vite.config.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defineConfig } from "vite";
2
- import motionCanvas from '@revideo/vite-plugin';
2
+ import motionCanvas from '@twick/vite-plugin';
3
3
 
4
4
  export default defineConfig({
5
5
  plugins: [motionCanvas()],
@@ -1,7 +0,0 @@
1
- import { all, Reference } from "@revideo/core";
2
-
3
- export function* addAnimations({ elementRef }: { elementRef: Reference<any>;}) {
4
- yield elementRef();
5
- const animations:any[] = []
6
- return yield* all(...animations);
7
- }
@@ -1,344 +0,0 @@
1
- /**
2
- * Element component that handles the creation and management of visual elements
3
- * with various properties like position, size, rotation, and effects.
4
- */
5
-
6
- import { createRef, waitFor, all, Reference, Color } from "@revideo/core";
7
- import { Audio, Circle, Icon, Img, Rect, Txt, Video } from "@revideo/2d";
8
- import { logger } from "../helpers/log.utils";
9
- import {
10
- DEFAULT_BACKGROUND_COLOR,
11
- ELEMENT_TYPES,
12
- TRANSPARENT_COLOR,
13
- } from "../helpers/constants";
14
- import { fitElement } from "../helpers/element.utils";
15
- import { applyColorFilter } from "../helpers/filters";
16
- import { addFrameEffect } from "./frame-effects";
17
- import { Caption, CaptionProps, VisualizerElement } from "../helpers/types";
18
- import { splitPhraseTiming } from "../helpers/caption.utils";
19
- import { addAnimations } from "./animation";
20
-
21
-
22
- export function* addMediaElement({
23
- containerRef,
24
- element,
25
- mediaType,
26
- waitOnStart = true,
27
- }: {
28
- containerRef: Reference<any>;
29
- element: VisualizerElement;
30
- mediaType: string;
31
- waitOnStart?: boolean;
32
- }) {
33
- if (waitOnStart) {
34
- yield* waitFor(element.s);
35
- }
36
- const frameContainerRef = createRef<any>();
37
- const frameElementRef = createRef<any>();
38
- if (mediaType === ELEMENT_TYPES.VIDEO) {
39
- const frameProps = element.frame || element.props;
40
- logger(`Adding video element ${element.id}`);
41
- yield containerRef().add(
42
- <Rect ref={frameContainerRef} key={element.id} {...frameProps}>
43
- <Video
44
- ref={frameElementRef}
45
- key={`child-${element.id}`}
46
- {...element.props}
47
- />
48
- </Rect>
49
- );
50
- } else if (mediaType === ELEMENT_TYPES.IMAGE) {
51
- logger(`Adding image element ${element.id}`);
52
- yield containerRef().add(
53
- <Rect ref={frameContainerRef} key={element.id} {...element.frame}>
54
- <Img
55
- ref={frameElementRef}
56
- key={`child-${element.id}`}
57
- {...element.props}
58
- />
59
- </Rect>
60
- );
61
- }
62
- if (frameContainerRef()) {
63
- yield fitElement({
64
- elementRef: frameElementRef,
65
- containerSize: frameContainerRef().size(),
66
- elementSize: frameElementRef().size(),
67
- objectFit: element.objectFit,
68
- });
69
-
70
- if (element?.props?.mediaFilter) {
71
- applyColorFilter(frameElementRef, element.props.mediaFilter);
72
- }
73
-
74
- yield* all(
75
- addAnimations({
76
- elementRef: frameElementRef,
77
- }),
78
- addFrameEffect({
79
- containerRef: frameContainerRef,
80
- elementRef: frameElementRef,
81
- frameElement,
82
- }),
83
- waitFor(Math.max(0, element.e - element.s))
84
- );
85
- if (element.type === ELEMENT_TYPES.VIDEO) {
86
- yield frameElementRef().playing(false);
87
- }
88
- yield frameElementRef().remove();
89
- yield frameContainerRef().remove();
90
- }
91
- }
92
-
93
- export function* addCaptionElement({
94
- caption,
95
- captionProps,
96
- containerRef,
97
- capStyle,
98
- }: {
99
- caption: Caption;
100
- captionProps: CaptionProps;
101
- containerRef: Reference<any>;
102
- capStyle: string;
103
- }) {
104
- const words = splitPhraseTiming(caption);
105
- let phraseStart = 0;
106
- if (words?.length) {
107
- phraseStart = words[0].s;
108
- }
109
- let wordsState: {
110
- refs: Array<{ bgRef?: Reference<any>; textRef: Reference<any> }>;
111
- props: CaptionProps[];
112
- idx: number;
113
- prevTime: number;
114
- } = {
115
- refs: [],
116
- props: [],
117
- idx: 0,
118
- prevTime: phraseStart,
119
- };
120
-
121
- for (const word of words) {
122
- wordsState.props.push(captionProps);
123
- const textRef = createRef<Txt>();
124
- containerRef().add(
125
- <Txt ref={textRef} {...captionProps} text={word.t} opacity={0} />
126
- );
127
- if (capStyle == "highlight_bg") {
128
- const bgContainerRef = createRef();
129
- const childTextRef = createRef();
130
- containerRef().add(
131
- <Rect
132
- ref={bgContainerRef}
133
- fill={new Color(captionProps.colors.background).alpha(
134
- captionProps?.bgOpacity ?? 1
135
- )}
136
- width={textRef().width() + (captionProps.bgOffsetWidth ?? 30)}
137
- height={textRef().height() + (captionProps.bgOffsetHeight ?? 10)}
138
- margin={captionProps.bgMargin ?? [0, -5]}
139
- radius={captionProps.bgRadius ?? 10}
140
- padding={captionProps.bgPadding ?? [0, 15]}
141
- opacity={0}
142
- alignItems={"center"}
143
- justifyContent={"center"}
144
- layout
145
- >
146
- <Txt ref={childTextRef} {...captionProps} text={word.t} />
147
- </Rect>
148
- );
149
- textRef().remove();
150
- wordsState.refs.push({
151
- bgRef: bgContainerRef,
152
- textRef: childTextRef,
153
- });
154
- } else {
155
- wordsState.refs.push({
156
- textRef: textRef,
157
- });
158
- }
159
- wordsState.prevTime = word.e;
160
- wordsState.idx = wordsState.idx + 1;
161
- }
162
-
163
- wordsState.prevTime = phraseStart;
164
- wordsState.idx = 0;
165
-
166
- for (const word of words) {
167
- if (capStyle == "highlight_bg") {
168
- yield* wordsState.refs[wordsState.idx]?.bgRef?.().opacity(1, 0);
169
- yield* waitFor(Math.max(0, word.e - word.s));
170
- yield* wordsState.refs[wordsState.idx]
171
- ?.bgRef?.()
172
- .fill(TRANSPARENT_COLOR, 0);
173
- } else {
174
- yield* wordsState.refs[wordsState.idx]?.textRef?.().opacity(1, 0);
175
- yield* waitFor(Math.max(0, word.e - word.s));
176
- }
177
- wordsState.prevTime = word.e;
178
- wordsState.idx = wordsState.idx + 1;
179
- }
180
- }
181
-
182
- export function* makeSceneElements({
183
- containerRef,
184
- element,
185
- }: {
186
- containerRef: Reference<any>;
187
- element: VisualizerElement;
188
- }) {
189
- switch (element.type) {
190
- case ELEMENT_TYPES.RECT:
191
- yield* addSceneRectElement({ containerRef, element });
192
- break;
193
- case ELEMENT_TYPES.IMAGE:
194
- case ELEMENT_TYPES.VIDEO:
195
- const mediaContainerRef = createRef<any>();
196
- yield containerRef().add(
197
- <Rect
198
- ref={mediaContainerRef}
199
- fill={element.backgroundColor ?? DEFAULT_BACKGROUND_COLOR}
200
- size={"100%"}
201
- />
202
- );
203
- yield* addMediaElement({
204
- containerRef: mediaContainerRef,
205
- element,
206
- mediaType: element.type,
207
- waitOnStart: false,
208
- });
209
- yield mediaContainerRef().remove();
210
- break;
211
- default:
212
- yield* waitFor(Math.max(0, element.e - element.s));
213
- break;
214
- }
215
- }
216
-
217
- function* addSceneRectElement({
218
- containerRef,
219
- element,
220
- }: {
221
- containerRef: Reference<any>;
222
- element: VisualizerElement;
223
- }) {
224
- const elementRef = createRef<any>();
225
- let sequence: any[] = [];
226
- yield containerRef().add(
227
- <Rect ref={elementRef} key={element.id} {...element.props}></Rect>
228
- );
229
- for (const childElement of element.elements || []) {
230
- sequence.push(
231
- makeSceneElements({
232
- containerRef: elementRef,
233
- element: childElement,
234
- })
235
- );
236
- }
237
-
238
- yield* all(
239
- ...sequence,
240
- addAnimations({
241
- elementRef,
242
- }),
243
- waitFor(Math.max(0, element.e - element.s))
244
- );
245
- yield elementRef().remove();
246
- }
247
-
248
- export function* addRectElement({
249
- containerRef,
250
- element,
251
- }: {
252
- containerRef: Reference<any>;
253
- element: VisualizerElement;
254
- }) {
255
- const elementRef = createRef<any>();
256
- yield* waitFor(element?.s);
257
- yield containerRef().add(
258
- <Rect ref={elementRef} key={element.id} {...element.props} />
259
- );
260
- yield* all(
261
- addAnimations({ elementRef: elementRef }),
262
- waitFor(Math.max(0, element.e - element.s))
263
- );
264
- yield elementRef().remove();
265
- }
266
-
267
- export function* addTextElement({
268
- containerRef,
269
- element,
270
- }: {
271
- containerRef: Reference<any>;
272
- element: VisualizerElement;
273
- }) {
274
- const elementRef = createRef<any>();
275
- yield* waitFor(element?.s);
276
- yield containerRef().add(
277
- <Txt
278
- ref={elementRef}
279
- key={element.id}
280
- text={element.t}
281
- {...element.props}
282
- />
283
- );
284
- yield* all(
285
- addAnimations({ elementRef: elementRef }),
286
- waitFor(Math.max(0, element.e - element.s))
287
- );
288
- yield elementRef().remove();
289
- }
290
-
291
- export function* addCircleElement({
292
- containerRef,
293
- element,
294
- }: {
295
- containerRef: Reference<any>;
296
- element: VisualizerElement;
297
- }) {
298
- const elementRef = createRef<any>();
299
- yield* waitFor(element?.s);
300
- yield containerRef().add(
301
- <Circle ref={elementRef} key={element.id} {...element.props} />
302
- );
303
- yield* all(
304
- addAnimations({ elementRef: elementRef }),
305
- waitFor(Math.max(0, element.e - element.s))
306
- );
307
- yield elementRef().remove();
308
- }
309
-
310
- export function* addIconElement({
311
- containerRef,
312
- element,
313
- }: {
314
- containerRef: Reference<any>;
315
- element: VisualizerElement;
316
- }) {
317
- const elementRef = createRef<any>();
318
- yield* waitFor(element?.s);
319
- yield containerRef().add(
320
- <Icon ref={elementRef} key={element.id} {...element.props} />
321
- );
322
- yield* all(
323
- addAnimations({ elementRef: elementRef }),
324
- waitFor(Math.max(0, element.e - element.s))
325
- );
326
- yield elementRef().remove();
327
- }
328
-
329
- export function* addAudioElement({
330
- containerRef,
331
- element,
332
- }: {
333
- containerRef: Reference<any>;
334
- element: VisualizerElement;
335
- }) {
336
- const elementRef = createRef<any>();
337
- yield* waitFor(element?.s);
338
- yield containerRef().add(
339
- <Audio ref={elementRef} key={element.id} {...element.props} />
340
- );
341
- yield* waitFor(Math.max(0, element.e - element.s));
342
- yield elementRef().playing(false);
343
- yield elementRef().remove();
344
- }