@twick/visualizer 0.0.1 → 0.14.2
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/.eslintrc.json +20 -20
- package/README.md +12 -12
- package/package.json +34 -31
- package/package.json.bak +34 -0
- package/src/animations/blur.tsx +60 -0
- package/src/animations/breathe.tsx +60 -0
- package/src/animations/fade.tsx +60 -0
- package/src/animations/photo-rise.tsx +66 -0
- package/src/animations/photo-zoom.tsx +73 -0
- package/src/animations/rise.tsx +118 -0
- package/src/animations/succession.tsx +77 -0
- package/src/components/frame-effects.tsx +188 -190
- package/src/components/track.tsx +237 -0
- package/src/controllers/animation.controller.ts +39 -0
- package/src/controllers/element.controller.ts +43 -0
- package/src/controllers/frame-effect.controller.tsx +30 -0
- package/src/controllers/text-effect.controller.ts +33 -0
- package/src/elements/audio.element.tsx +17 -0
- package/src/elements/caption.element.tsx +87 -0
- package/src/elements/circle.element.tsx +20 -0
- package/src/elements/icon.element.tsx +20 -0
- package/src/elements/image.element.tsx +53 -0
- package/src/elements/rect.element.tsx +22 -0
- package/src/elements/scene.element.tsx +29 -0
- package/src/elements/text.element.tsx +28 -0
- package/src/elements/video.element.tsx +55 -0
- package/src/frame-effects/circle.frame.tsx +103 -0
- package/src/frame-effects/rect.frame.tsx +103 -0
- package/src/global.css +11 -11
- package/src/helpers/caption.utils.ts +30 -40
- package/src/helpers/constants.ts +162 -158
- package/src/helpers/element.utils.ts +239 -85
- package/src/helpers/event.utils.ts +6 -0
- package/src/helpers/filters.ts +127 -127
- package/src/helpers/log.utils.ts +29 -32
- package/src/helpers/timing.utils.ts +110 -129
- package/src/helpers/types.ts +242 -146
- package/src/helpers/utils.ts +20 -0
- package/src/index.ts +6 -4
- package/src/live.tsx +16 -16
- package/src/project.ts +6 -6
- package/src/sample.ts +247 -449
- package/src/text-effects/elastic.tsx +39 -0
- package/src/text-effects/erase.tsx +58 -0
- package/src/text-effects/stream-word.tsx +60 -0
- package/src/text-effects/typewriter.tsx +59 -0
- package/src/visualizer.tsx +98 -78
- package/tsconfig.json +11 -10
- package/typedoc.json +14 -14
- package/vite.config.ts +15 -15
- package/src/components/animation.tsx +0 -7
- package/src/components/element.tsx +0 -344
- package/src/components/timeline.tsx +0 -225
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Track component that handles the creation and management of different types of tracks
|
|
3
|
+
* including video, audio, captions, scenes, and elements.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Layout, Rect, View2D, Audio } from "@twick/2d";
|
|
7
|
+
import { VisualizerTrack } from "../helpers/types";
|
|
8
|
+
import { all, Color, createRef, ThreadGenerator, waitFor } from "@twick/core";
|
|
9
|
+
import {
|
|
10
|
+
CAPTION_STYLE,
|
|
11
|
+
DEFAULT_CAPTION_COLORS,
|
|
12
|
+
DEFAULT_CAPTION_FONT,
|
|
13
|
+
} from "../helpers/constants";
|
|
14
|
+
import { logger } from "../helpers/log.utils";
|
|
15
|
+
import elementController from "../controllers/element.controller";
|
|
16
|
+
import { hexToRGB } from "../helpers/utils";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a video track with specified configuration
|
|
20
|
+
* @param {Object} params - Parameters for video track creation
|
|
21
|
+
* @param {View2D} params.view - The 2D view to render the video in
|
|
22
|
+
* @param {VideoTrack} params.track - Video track configuration
|
|
23
|
+
* @returns {Generator} Generator function for video track animation
|
|
24
|
+
*/
|
|
25
|
+
export function* makeVideoTrack({
|
|
26
|
+
view,
|
|
27
|
+
track,
|
|
28
|
+
}: {
|
|
29
|
+
view: View2D;
|
|
30
|
+
track: VisualizerTrack;
|
|
31
|
+
}) {
|
|
32
|
+
const frameRef = createRef<any>();
|
|
33
|
+
let prevTime = 0;
|
|
34
|
+
view.add(<Layout size={"100%"} ref={frameRef} layout />);
|
|
35
|
+
for (const element of track.elements || []) {
|
|
36
|
+
yield* waitFor(element?.s - prevTime);
|
|
37
|
+
yield* elementController.get("video")?.create({
|
|
38
|
+
containerRef: frameRef,
|
|
39
|
+
element,
|
|
40
|
+
view,
|
|
41
|
+
});
|
|
42
|
+
prevTime = element.e;
|
|
43
|
+
}
|
|
44
|
+
yield frameRef().remove();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates an audio track with specified configuration
|
|
49
|
+
* @param {Object} params - Parameters for audio track creation
|
|
50
|
+
* @param {View2D} params.view - The 2D view to render the audio in
|
|
51
|
+
* @param {AudioTrack} params.track - Audio track configuration
|
|
52
|
+
* @returns {Generator} Generator function for audio track animation
|
|
53
|
+
*/
|
|
54
|
+
export function* makeAudioTrack({
|
|
55
|
+
view,
|
|
56
|
+
track,
|
|
57
|
+
}: {
|
|
58
|
+
view: View2D;
|
|
59
|
+
track: VisualizerTrack;
|
|
60
|
+
}) {
|
|
61
|
+
let prevTime = 0;
|
|
62
|
+
for (const audioElement of track.elements) {
|
|
63
|
+
const audioRef = createRef<any>();
|
|
64
|
+
yield* waitFor(audioElement?.s - prevTime);
|
|
65
|
+
prevTime = audioElement?.e;
|
|
66
|
+
logger(`Adding audio element ${audioElement.id}`);
|
|
67
|
+
view.add(
|
|
68
|
+
<Audio ref={audioRef} key={audioElement.id} {...audioElement.props} />
|
|
69
|
+
);
|
|
70
|
+
yield* waitFor(Math.max(0, audioElement.e - audioElement.s));
|
|
71
|
+
yield audioRef().playing(false);
|
|
72
|
+
yield audioRef().remove();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Creates a caption track with specified configuration
|
|
78
|
+
* @param {Object} params - Parameters for caption track creation
|
|
79
|
+
* @param {View2D} params.view - The 2D view to render the caption in
|
|
80
|
+
* @param {CaptionTrack} params.track - Caption track configuration
|
|
81
|
+
* @returns {Generator} Generator function for caption track animation
|
|
82
|
+
*/
|
|
83
|
+
export function* makeCaptionTrack({
|
|
84
|
+
view,
|
|
85
|
+
track,
|
|
86
|
+
}: {
|
|
87
|
+
view: View2D;
|
|
88
|
+
track: VisualizerTrack;
|
|
89
|
+
}) {
|
|
90
|
+
let prevTime = 0;
|
|
91
|
+
const captionTrackRef = createRef<any>();
|
|
92
|
+
view.add(<Layout size={"100%"} ref={captionTrackRef} />);
|
|
93
|
+
|
|
94
|
+
const tProps = track?.props;
|
|
95
|
+
|
|
96
|
+
const trackDefaultProps =
|
|
97
|
+
(CAPTION_STYLE[tProps?.capStyle ?? ""] || {}).word || {};
|
|
98
|
+
|
|
99
|
+
for (const element of track.elements) {
|
|
100
|
+
const eProps = element.props;
|
|
101
|
+
const rectStyle =
|
|
102
|
+
(CAPTION_STYLE[eProps?.capStyle ?? tProps?.capStyle ?? ""] || {}).rect ||
|
|
103
|
+
{};
|
|
104
|
+
// Cast alignItems/justifyContent as any to satisfy RectProps
|
|
105
|
+
const mappedRectStyle = {
|
|
106
|
+
...rectStyle,
|
|
107
|
+
justifyContent: rectStyle.justifyContent as any,
|
|
108
|
+
alignItems: rectStyle.alignItems as any,
|
|
109
|
+
};
|
|
110
|
+
const phraseProps = {
|
|
111
|
+
...trackDefaultProps,
|
|
112
|
+
colors: {
|
|
113
|
+
text:
|
|
114
|
+
eProps?.colors?.text ??
|
|
115
|
+
tProps?.colors?.text ??
|
|
116
|
+
DEFAULT_CAPTION_COLORS.text,
|
|
117
|
+
background:
|
|
118
|
+
eProps?.colors?.background ??
|
|
119
|
+
tProps?.colors?.background ??
|
|
120
|
+
DEFAULT_CAPTION_COLORS.background,
|
|
121
|
+
},
|
|
122
|
+
font: {
|
|
123
|
+
family:
|
|
124
|
+
eProps?.font?.family ??
|
|
125
|
+
tProps?.font?.family ??
|
|
126
|
+
DEFAULT_CAPTION_FONT.family,
|
|
127
|
+
size:
|
|
128
|
+
eProps?.font?.size ?? tProps?.font?.size ?? DEFAULT_CAPTION_FONT.size,
|
|
129
|
+
weight:
|
|
130
|
+
eProps?.font?.weight ??
|
|
131
|
+
tProps?.font?.weight ??
|
|
132
|
+
DEFAULT_CAPTION_FONT.weight,
|
|
133
|
+
},
|
|
134
|
+
fill:
|
|
135
|
+
eProps?.colors?.text ??
|
|
136
|
+
tProps?.colors?.text ??
|
|
137
|
+
DEFAULT_CAPTION_COLORS.text,
|
|
138
|
+
bgColor:
|
|
139
|
+
eProps?.colors?.background ??
|
|
140
|
+
tProps?.colors?.background ??
|
|
141
|
+
DEFAULT_CAPTION_COLORS.background,
|
|
142
|
+
bgOpacity: eProps?.bgOpacity ?? tProps?.bgOpacity ?? 1,
|
|
143
|
+
...(tProps?.captionProps || {}),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
yield* waitFor(element?.s - prevTime);
|
|
147
|
+
const phraseRef = createRef<any>();
|
|
148
|
+
captionTrackRef().add(
|
|
149
|
+
<Rect
|
|
150
|
+
ref={phraseRef}
|
|
151
|
+
key={element.id}
|
|
152
|
+
{...mappedRectStyle}
|
|
153
|
+
x={eProps?.x ?? tProps?.x}
|
|
154
|
+
y={eProps?.y ?? tProps?.y}
|
|
155
|
+
layout
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
if (tProps?.capStyle === "word_by_word_with_bg") {
|
|
159
|
+
const _color = new Color({...hexToRGB(phraseProps.bgColor), a: phraseProps?.bgOpacity ?? 1});
|
|
160
|
+
phraseRef().fill(_color);
|
|
161
|
+
}
|
|
162
|
+
yield* elementController.get("caption")?.create({
|
|
163
|
+
containerRef: phraseRef,
|
|
164
|
+
caption: {
|
|
165
|
+
...element,
|
|
166
|
+
t: element.t ?? "",
|
|
167
|
+
capStyle: eProps?.capStyle ?? tProps?.capStyle,
|
|
168
|
+
props: phraseProps,
|
|
169
|
+
},
|
|
170
|
+
view,
|
|
171
|
+
});
|
|
172
|
+
prevTime = element.e;
|
|
173
|
+
yield phraseRef().remove();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Creates a scene track with specified configuration
|
|
179
|
+
* @param {Object} params - Parameters for scene track creation
|
|
180
|
+
* @param {View2D} params.view - The 2D view to render the scene in
|
|
181
|
+
* @param {SceneTrack} params.track - Scene track configuration
|
|
182
|
+
* @returns {Generator} Generator function for scene track animation
|
|
183
|
+
*/
|
|
184
|
+
export function* makeSceneTrack({
|
|
185
|
+
view,
|
|
186
|
+
track,
|
|
187
|
+
}: {
|
|
188
|
+
view: View2D;
|
|
189
|
+
track: VisualizerTrack;
|
|
190
|
+
}) {
|
|
191
|
+
const frameRef = createRef<any>();
|
|
192
|
+
view.add(<Layout size={"100%"} ref={frameRef} layout />);
|
|
193
|
+
for (const sceneElement of track.elements || []) {
|
|
194
|
+
yield* elementController.get("scene")?.create({
|
|
195
|
+
containerRef: frameRef,
|
|
196
|
+
element: sceneElement,
|
|
197
|
+
view,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
yield frameRef().remove();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Creates an element track with specified configuration
|
|
205
|
+
* @param {Object} params - Parameters for element track creation
|
|
206
|
+
* @param {View2D} params.view - The 2D view to render the element in
|
|
207
|
+
* @param {ElementTrack} params.track - Element track configuration
|
|
208
|
+
* @returns {Generator} Generator function for element track animation
|
|
209
|
+
*/
|
|
210
|
+
export function* makeElementTrack({
|
|
211
|
+
view,
|
|
212
|
+
track,
|
|
213
|
+
}: {
|
|
214
|
+
view: View2D;
|
|
215
|
+
track: VisualizerTrack;
|
|
216
|
+
}) {
|
|
217
|
+
const elementTrackRef = createRef<any>();
|
|
218
|
+
view.add(<Layout size={"100%"} ref={elementTrackRef} />);
|
|
219
|
+
|
|
220
|
+
const sequence: ThreadGenerator[] = [];
|
|
221
|
+
try {
|
|
222
|
+
for (const element of track.elements) {
|
|
223
|
+
sequence.push(
|
|
224
|
+
elementController.get(element.type)?.create({
|
|
225
|
+
containerRef: elementTrackRef,
|
|
226
|
+
element,
|
|
227
|
+
view,
|
|
228
|
+
})
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
logger("Error creating element track", error);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
yield* all(...sequence);
|
|
236
|
+
yield elementTrackRef().remove();
|
|
237
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BlurAnimation } from "../animations/blur";
|
|
2
|
+
import { BreatheAnimation } from "../animations/breathe";
|
|
3
|
+
import { FadeAnimation } from "../animations/fade";
|
|
4
|
+
import { PhotoRiseAnimation } from "../animations/photo-rise";
|
|
5
|
+
import { PhotoZoomAnimation } from "../animations/photo-zoom";
|
|
6
|
+
import { RiseAnimation } from "../animations/rise";
|
|
7
|
+
import { SuccessionAnimation } from "../animations/succession";
|
|
8
|
+
import { Animation } from "../helpers/types";
|
|
9
|
+
|
|
10
|
+
export class AnimationController {
|
|
11
|
+
private animations: Map<string, Animation> = new Map();
|
|
12
|
+
|
|
13
|
+
register(animation: Animation) {
|
|
14
|
+
this.animations.set(animation.name, animation);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get(name: string): Animation | undefined {
|
|
18
|
+
return this.animations.get(name);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
list(): string[] {
|
|
22
|
+
return Array.from(this.animations.keys());
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const registerAnimations = () => {
|
|
27
|
+
animationController.register(FadeAnimation);
|
|
28
|
+
animationController.register(RiseAnimation);
|
|
29
|
+
animationController.register(BreatheAnimation);
|
|
30
|
+
animationController.register(SuccessionAnimation);
|
|
31
|
+
animationController.register(BlurAnimation);
|
|
32
|
+
animationController.register(PhotoZoomAnimation);
|
|
33
|
+
animationController.register(PhotoRiseAnimation);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const animationController = new AnimationController();
|
|
37
|
+
registerAnimations();
|
|
38
|
+
|
|
39
|
+
export default animationController;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { AudioElement } from "../elements/audio.element";
|
|
2
|
+
import { CaptionElement } from "../elements/caption.element";
|
|
3
|
+
import { CircleElement } from "../elements/circle.element";
|
|
4
|
+
import { IconElement } from "../elements/icon.element";
|
|
5
|
+
import { ImageElement } from "../elements/image.element";
|
|
6
|
+
import { RectElement } from "../elements/rect.element";
|
|
7
|
+
import { SceneElement } from "../elements/scene.element";
|
|
8
|
+
import { TextElement } from "../elements/text.element";
|
|
9
|
+
import { VideoElement } from "../elements/video.element";
|
|
10
|
+
import { Element } from "../helpers/types";
|
|
11
|
+
|
|
12
|
+
export class ElementController {
|
|
13
|
+
private elements: Map<string, Element> = new Map();
|
|
14
|
+
|
|
15
|
+
register(element: Element) {
|
|
16
|
+
this.elements.set(element.name, element);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get(name: string): Element | undefined {
|
|
20
|
+
return this.elements.get(name);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
list(): string[] {
|
|
24
|
+
return Array.from(this.elements.keys());
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const registerElements = () => {
|
|
29
|
+
elementController.register(VideoElement);
|
|
30
|
+
elementController.register(CaptionElement);
|
|
31
|
+
elementController.register(SceneElement);
|
|
32
|
+
elementController.register(ImageElement);
|
|
33
|
+
elementController.register(TextElement);
|
|
34
|
+
elementController.register(AudioElement);
|
|
35
|
+
elementController.register(CircleElement);
|
|
36
|
+
elementController.register(IconElement);
|
|
37
|
+
elementController.register(RectElement);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const elementController = new ElementController();
|
|
41
|
+
registerElements();
|
|
42
|
+
|
|
43
|
+
export default elementController;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
import { CircleFrameEffect } from "../frame-effects/circle.frame";
|
|
3
|
+
import { RectFrameEffect } from "../frame-effects/rect.frame";
|
|
4
|
+
import { FrameEffectPlugin } from "../helpers/types";
|
|
5
|
+
|
|
6
|
+
export class FrameEffectController {
|
|
7
|
+
private frameEffects: Map<string, FrameEffectPlugin> = new Map();
|
|
8
|
+
|
|
9
|
+
register(frameEffect: FrameEffectPlugin) {
|
|
10
|
+
this.frameEffects.set(frameEffect.name, frameEffect);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get(name: string): FrameEffectPlugin | undefined {
|
|
14
|
+
return this.frameEffects.get(name);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
list(): string[] {
|
|
18
|
+
return Array.from(this.frameEffects.keys());
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const registerFrameEffects = () => {
|
|
23
|
+
frameEffectController.register(CircleFrameEffect);
|
|
24
|
+
frameEffectController.register(RectFrameEffect);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const frameEffectController = new FrameEffectController();
|
|
28
|
+
registerFrameEffects();
|
|
29
|
+
|
|
30
|
+
export default frameEffectController;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ElasticEffect } from "../text-effects/elastic";
|
|
2
|
+
import { EraseEffect } from "../text-effects/erase";
|
|
3
|
+
import { StreamWordEffect } from "../text-effects/stream-word";
|
|
4
|
+
import { TypewriterEffect } from "../text-effects/typewriter";
|
|
5
|
+
import { TextEffect } from "../helpers/types";
|
|
6
|
+
|
|
7
|
+
export class TextEffectController {
|
|
8
|
+
private effects: Map<string, TextEffect> = new Map();
|
|
9
|
+
|
|
10
|
+
register(effect: TextEffect) {
|
|
11
|
+
this.effects.set(effect.name, effect);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get(name: string): TextEffect | undefined {
|
|
15
|
+
return this.effects.get(name);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
list(): string[] {
|
|
19
|
+
return Array.from(this.effects.keys());
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const registerTextEffects = () => {
|
|
24
|
+
textEffectController.register(TypewriterEffect);
|
|
25
|
+
textEffectController.register(StreamWordEffect);
|
|
26
|
+
textEffectController.register(EraseEffect);
|
|
27
|
+
textEffectController.register(ElasticEffect);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const textEffectController = new TextEffectController();
|
|
31
|
+
registerTextEffects();
|
|
32
|
+
|
|
33
|
+
export default textEffectController;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ElementParams } from "../helpers/types";
|
|
2
|
+
import { createRef, waitFor } from "@twick/core";
|
|
3
|
+
import { Audio } from "@twick/2d";
|
|
4
|
+
|
|
5
|
+
export const AudioElement = {
|
|
6
|
+
name: "audio",
|
|
7
|
+
*create({ containerRef, element, view }: ElementParams) {
|
|
8
|
+
const elementRef = createRef<any>();
|
|
9
|
+
yield* waitFor(element?.s);
|
|
10
|
+
yield containerRef().add(
|
|
11
|
+
<Audio ref={elementRef} key={element.id} play={true} {...element.props} />
|
|
12
|
+
);
|
|
13
|
+
yield* waitFor(Math.max(0, element.e - element.s));
|
|
14
|
+
yield elementRef().play(false);
|
|
15
|
+
yield elementRef().remove();
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { CaptionProps, ElementParams } from "../helpers/types";
|
|
2
|
+
import { Color, createRef, Reference, waitFor } from "@twick/core";
|
|
3
|
+
import { Rect, Txt } from "@twick/2d";
|
|
4
|
+
import { splitPhraseTiming } from "../helpers/caption.utils";
|
|
5
|
+
import { TRANSPARENT_COLOR } from "../helpers/constants";
|
|
6
|
+
import { hexToRGB } from "../helpers/utils";
|
|
7
|
+
|
|
8
|
+
export const CaptionElement = {
|
|
9
|
+
name: "caption",
|
|
10
|
+
*create({ containerRef, caption }: ElementParams) {
|
|
11
|
+
const words = splitPhraseTiming(caption);
|
|
12
|
+
let phraseStart = 0;
|
|
13
|
+
if (words?.length) {
|
|
14
|
+
phraseStart = words[0].s;
|
|
15
|
+
}
|
|
16
|
+
let wordsState: {
|
|
17
|
+
refs: Array<{ bgRef?: Reference<any>; textRef: Reference<any> }>;
|
|
18
|
+
props: CaptionProps[];
|
|
19
|
+
idx: number;
|
|
20
|
+
prevTime: number;
|
|
21
|
+
} = {
|
|
22
|
+
refs: [],
|
|
23
|
+
props: [],
|
|
24
|
+
idx: 0,
|
|
25
|
+
prevTime: phraseStart,
|
|
26
|
+
};
|
|
27
|
+
for (const word of words) {
|
|
28
|
+
wordsState.props.push(caption.props);
|
|
29
|
+
const textRef = createRef<Txt>();
|
|
30
|
+
const captionProps = caption.props;
|
|
31
|
+
containerRef().add(
|
|
32
|
+
<Txt ref={textRef} {...captionProps} text={word.t} opacity={0} />
|
|
33
|
+
);
|
|
34
|
+
if (caption.capStyle == "highlight_bg") {
|
|
35
|
+
const bgContainerRef = createRef();
|
|
36
|
+
const childTextRef = createRef();
|
|
37
|
+
const _color = new Color({...hexToRGB(captionProps.colors.background), a: captionProps?.bgOpacity ?? 1});
|
|
38
|
+
containerRef().add(
|
|
39
|
+
<Rect
|
|
40
|
+
ref={bgContainerRef}
|
|
41
|
+
fill={_color}
|
|
42
|
+
width={textRef().width() + (captionProps.bgOffsetWidth ?? 30)}
|
|
43
|
+
height={textRef().height() + (captionProps.bgOffsetHeight ?? 10)}
|
|
44
|
+
margin={captionProps.bgMargin ?? [0, -5]}
|
|
45
|
+
radius={captionProps.bgRadius ?? 10}
|
|
46
|
+
padding={captionProps.bgPadding ?? [0, 15]}
|
|
47
|
+
opacity={0}
|
|
48
|
+
alignItems={"center"}
|
|
49
|
+
justifyContent={"center"}
|
|
50
|
+
layout
|
|
51
|
+
>
|
|
52
|
+
<Txt ref={childTextRef} {...captionProps} text={word.t} />
|
|
53
|
+
</Rect>
|
|
54
|
+
);
|
|
55
|
+
textRef().remove();
|
|
56
|
+
wordsState.refs.push({
|
|
57
|
+
bgRef: bgContainerRef,
|
|
58
|
+
textRef: childTextRef,
|
|
59
|
+
});
|
|
60
|
+
} else {
|
|
61
|
+
wordsState.refs.push({
|
|
62
|
+
textRef: textRef,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
wordsState.prevTime = word.e;
|
|
66
|
+
wordsState.idx = wordsState.idx + 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
wordsState.prevTime = phraseStart;
|
|
70
|
+
wordsState.idx = 0;
|
|
71
|
+
|
|
72
|
+
for (const word of words) {
|
|
73
|
+
if (caption.capStyle == "highlight_bg") {
|
|
74
|
+
yield* wordsState.refs[wordsState.idx]?.bgRef?.().opacity(1, 0);
|
|
75
|
+
yield* waitFor(Math.max(0, word.e - word.s));
|
|
76
|
+
yield* wordsState.refs[wordsState.idx]
|
|
77
|
+
?.bgRef?.()
|
|
78
|
+
.fill(TRANSPARENT_COLOR, 0);
|
|
79
|
+
} else {
|
|
80
|
+
yield* wordsState.refs[wordsState.idx]?.textRef?.().opacity(1, 0);
|
|
81
|
+
yield* waitFor(Math.max(0, word.e - word.s));
|
|
82
|
+
}
|
|
83
|
+
wordsState.prevTime = word.e;
|
|
84
|
+
wordsState.idx = wordsState.idx + 1;
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ElementParams } from "../helpers/types";
|
|
2
|
+
import { all, createRef, waitFor } from "@twick/core";
|
|
3
|
+
import { Circle } from "@twick/2d";
|
|
4
|
+
import { addAnimation } from "../helpers/element.utils";
|
|
5
|
+
|
|
6
|
+
export const CircleElement = {
|
|
7
|
+
name: "circle",
|
|
8
|
+
*create({ containerRef, element, view }: ElementParams) {
|
|
9
|
+
const elementRef = createRef<any>();
|
|
10
|
+
yield* waitFor(element?.s);
|
|
11
|
+
yield containerRef().add(
|
|
12
|
+
<Circle ref={elementRef} key={element.id} {...element.props} />
|
|
13
|
+
);
|
|
14
|
+
yield* all(
|
|
15
|
+
addAnimation({ elementRef: elementRef, element: element, view }),
|
|
16
|
+
waitFor(Math.max(0, element.e - element.s))
|
|
17
|
+
);
|
|
18
|
+
yield elementRef().remove();
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ElementParams } from "../helpers/types";
|
|
2
|
+
import { all, createRef, waitFor } from "@twick/core";
|
|
3
|
+
import { Icon } from "@twick/2d";
|
|
4
|
+
import { addAnimation } from "../helpers/element.utils";
|
|
5
|
+
|
|
6
|
+
export const IconElement = {
|
|
7
|
+
name: "icon",
|
|
8
|
+
*create({ containerRef, element, view }: ElementParams) {
|
|
9
|
+
const elementRef = createRef<any>();
|
|
10
|
+
yield* waitFor(element?.s);
|
|
11
|
+
yield containerRef().add(
|
|
12
|
+
<Icon ref={elementRef} key={element.id} {...element.props} />
|
|
13
|
+
);
|
|
14
|
+
yield* all(
|
|
15
|
+
addAnimation({ elementRef: elementRef, element: element, view }),
|
|
16
|
+
waitFor(Math.max(0, element.e - element.s))
|
|
17
|
+
);
|
|
18
|
+
yield elementRef().remove();
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ElementParams } from "../helpers/types";
|
|
2
|
+
import { all, createRef, waitFor } from "@twick/core";
|
|
3
|
+
import { Img, Rect } from "@twick/2d";
|
|
4
|
+
import { addAnimation, addFrameEffect, fitElement } from "../helpers/element.utils";
|
|
5
|
+
import { applyColorFilter } from "../helpers/filters";
|
|
6
|
+
|
|
7
|
+
export const ImageElement = {
|
|
8
|
+
name: "image",
|
|
9
|
+
*create({ containerRef, element, view }: ElementParams) {
|
|
10
|
+
yield* waitFor(element?.s);
|
|
11
|
+
const frameContainerRef = createRef<any>();
|
|
12
|
+
const frameElementRef = createRef<any>();
|
|
13
|
+
|
|
14
|
+
yield containerRef().add(
|
|
15
|
+
<Rect ref={frameContainerRef} key={element.id} {...element.frame}>
|
|
16
|
+
<Img
|
|
17
|
+
ref={frameElementRef}
|
|
18
|
+
key={`child-${element.id}`}
|
|
19
|
+
{...element.props}
|
|
20
|
+
/>
|
|
21
|
+
</Rect>
|
|
22
|
+
);
|
|
23
|
+
if (frameContainerRef()) {
|
|
24
|
+
yield fitElement({
|
|
25
|
+
elementRef: frameElementRef,
|
|
26
|
+
containerSize: frameContainerRef().size(),
|
|
27
|
+
elementSize: frameElementRef().size(),
|
|
28
|
+
objectFit: element.objectFit,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (element?.props?.mediaFilter) {
|
|
32
|
+
applyColorFilter(frameElementRef, element.props.mediaFilter);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
yield* all(
|
|
36
|
+
addAnimation({
|
|
37
|
+
elementRef: frameElementRef,
|
|
38
|
+
containerRef: frameContainerRef,
|
|
39
|
+
element: element,
|
|
40
|
+
view,
|
|
41
|
+
}),
|
|
42
|
+
addFrameEffect({
|
|
43
|
+
containerRef: frameContainerRef,
|
|
44
|
+
elementRef: frameElementRef,
|
|
45
|
+
element,
|
|
46
|
+
}),
|
|
47
|
+
waitFor(Math.max(0, element.e - element.s))
|
|
48
|
+
);
|
|
49
|
+
yield frameElementRef().remove();
|
|
50
|
+
yield frameContainerRef().remove();
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ElementParams } from "../helpers/types";
|
|
2
|
+
import { all, createRef, waitFor } from "@twick/core";
|
|
3
|
+
import { Rect } from "@twick/2d";
|
|
4
|
+
import { addAnimation } from "../helpers/element.utils";
|
|
5
|
+
import { logger } from "../helpers/log.utils";
|
|
6
|
+
|
|
7
|
+
export const RectElement = {
|
|
8
|
+
name: "rect",
|
|
9
|
+
*create({ containerRef, element, view }: ElementParams) {
|
|
10
|
+
const elementRef = createRef<any>();
|
|
11
|
+
yield* waitFor(element?.s);
|
|
12
|
+
logger(`RectElement: ${JSON.stringify(element)}`);
|
|
13
|
+
yield containerRef().add(
|
|
14
|
+
<Rect ref={elementRef} key={element.id} {...element.props}/>
|
|
15
|
+
);
|
|
16
|
+
yield* all(
|
|
17
|
+
addAnimation({ elementRef: elementRef, element: element, view }),
|
|
18
|
+
waitFor(Math.max(0, element.e - element.s))
|
|
19
|
+
);
|
|
20
|
+
yield elementRef().remove();
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ElementParams } from "../helpers/types";
|
|
2
|
+
import { createRef, waitFor } from "@twick/core";
|
|
3
|
+
import { Rect } from "@twick/2d";
|
|
4
|
+
import { DEFAULT_BACKGROUND_COLOR, ELEMENT_TYPES } from "../helpers/constants";
|
|
5
|
+
import { ImageElement } from "./image.element";
|
|
6
|
+
import { VideoElement } from "./video.element";
|
|
7
|
+
import { logger } from "../helpers/log.utils";
|
|
8
|
+
|
|
9
|
+
export const SceneElement = {
|
|
10
|
+
name: "scene",
|
|
11
|
+
*create({ containerRef, element, view }: ElementParams) {
|
|
12
|
+
yield* waitFor(element?.s);
|
|
13
|
+
const mediaContainerRef = createRef<any>();
|
|
14
|
+
logger(`SceneElement: ${JSON.stringify(element)}`);
|
|
15
|
+
yield containerRef().add(
|
|
16
|
+
<Rect
|
|
17
|
+
ref={mediaContainerRef}
|
|
18
|
+
fill={element.backgroundColor ?? DEFAULT_BACKGROUND_COLOR}
|
|
19
|
+
size={"100%"}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
if (element.type === ELEMENT_TYPES.IMAGE) {
|
|
23
|
+
yield* ImageElement.create({ containerRef, element, view });
|
|
24
|
+
} else if (element.type === ELEMENT_TYPES.VIDEO) {
|
|
25
|
+
yield* VideoElement.create({ containerRef, element, view });
|
|
26
|
+
}
|
|
27
|
+
yield mediaContainerRef().remove();
|
|
28
|
+
},
|
|
29
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ElementParams } from "../helpers/types";
|
|
2
|
+
import { all, createRef, waitFor } from "@twick/core";
|
|
3
|
+
import { Txt } from "@twick/2d";
|
|
4
|
+
import { addAnimation, addTextEffect } from "../helpers/element.utils";
|
|
5
|
+
|
|
6
|
+
export const TextElement = {
|
|
7
|
+
name: "text",
|
|
8
|
+
*create({ containerRef, element, view }: ElementParams) {
|
|
9
|
+
const elementRef = createRef<any>();
|
|
10
|
+
|
|
11
|
+
yield* waitFor(element?.s);
|
|
12
|
+
yield containerRef().add(
|
|
13
|
+
<Txt
|
|
14
|
+
ref={elementRef}
|
|
15
|
+
key={element.id}
|
|
16
|
+
text={element.t}
|
|
17
|
+
{...element.props}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
yield* all(
|
|
21
|
+
addAnimation({ elementRef: elementRef, element: element, view }),
|
|
22
|
+
addTextEffect({ elementRef: elementRef, element: element }),
|
|
23
|
+
waitFor(Math.max(0, element.e - element.s))
|
|
24
|
+
);
|
|
25
|
+
yield elementRef().remove();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|