@twick/visualizer 0.15.13 → 0.15.15
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/project.js +56 -56
- package/package.json +8 -8
- package/src/animations/fade.tsx +2 -2
- package/src/components/track.tsx +28 -2
- package/src/controllers/element.controller.ts +0 -2
- package/src/controllers/watermark.controller.ts +35 -0
- package/src/elements/index.ts +0 -1
- package/src/elements/text.element.tsx +80 -21
- package/src/helpers/types.ts +32 -3
- package/src/helpers/watermark.types.ts +23 -0
- package/src/index.ts +5 -0
- package/src/sample.ts +12 -0
- package/src/visualizer-grouped.ts +0 -1
- package/src/visualizer.tsx +21 -1
- package/src/watermark-renderers/image.watermark.tsx +37 -0
- package/src/watermark-renderers/index.ts +7 -0
- package/src/watermark-renderers/text.watermark.tsx +43 -0
- package/src/elements/icon.element.tsx +0 -88
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twick/visualizer",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.15",
|
|
4
4
|
"license": "https://github.com/ncounterspecialist/twick/blob/main/LICENSE.md",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"start": "twick editor --projectFile ./src/live.tsx",
|
|
@@ -22,18 +22,18 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@preact/signals": "^1.2.1",
|
|
25
|
-
"@twick/2d": "^0.15.
|
|
26
|
-
"@twick/core": "^0.15.
|
|
27
|
-
"@twick/renderer": "^0.15.
|
|
28
|
-
"@twick/vite-plugin": "^0.15.
|
|
25
|
+
"@twick/2d": "^0.15.15",
|
|
26
|
+
"@twick/core": "^0.15.15",
|
|
27
|
+
"@twick/renderer": "^0.15.15",
|
|
28
|
+
"@twick/vite-plugin": "^0.15.15",
|
|
29
29
|
"date-fns": "^4.1.0",
|
|
30
30
|
"preact": "^10.19.2",
|
|
31
31
|
"crelt": "^1.0.6",
|
|
32
|
-
"@twick/media-utils": "0.15.
|
|
32
|
+
"@twick/media-utils": "0.15.15"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@twick/cli": "^0.15.
|
|
36
|
-
"@twick/ui": "^0.15.
|
|
35
|
+
"@twick/cli": "^0.15.15",
|
|
36
|
+
"@twick/ui": "^0.15.15",
|
|
37
37
|
"typescript": "5.4.2",
|
|
38
38
|
"typedoc": "^0.25.8",
|
|
39
39
|
"typedoc-plugin-markdown": "^3.17.1",
|
package/src/animations/fade.tsx
CHANGED
|
@@ -74,10 +74,10 @@ import { AnimationParams } from "../helpers/types";
|
|
|
74
74
|
* animation: { name: "fade", animate: "enter", duration: 2 }
|
|
75
75
|
* },
|
|
76
76
|
* {
|
|
77
|
-
* id: "
|
|
77
|
+
* id: "caption",
|
|
78
78
|
* type: "text",
|
|
79
79
|
* s: 1, e: 8,
|
|
80
|
-
* t: "
|
|
80
|
+
* t: "Caption",
|
|
81
81
|
* animation: { name: "fade", animate: "enter", duration: 2 }
|
|
82
82
|
* },
|
|
83
83
|
* {
|
package/src/components/track.tsx
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* including video, audio, captions, scenes, and elements.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Layout, Rect, View2D, Audio } from "@twick/2d";
|
|
7
|
-
import { VisualizerTrack } from "../helpers/types";
|
|
6
|
+
import { Layout, Rect, View2D, Audio, Img, Txt } from "@twick/2d";
|
|
7
|
+
import { VisualizerTrack, WatermarkInput } from "../helpers/types";
|
|
8
8
|
import { all, Color, createRef, ThreadGenerator, waitFor } from "@twick/core";
|
|
9
9
|
import {
|
|
10
10
|
CAPTION_STYLE,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "../helpers/constants";
|
|
14
14
|
import { logger } from "../helpers/log.utils";
|
|
15
15
|
import elementController from "../controllers/element.controller";
|
|
16
|
+
import watermarkController from "../controllers/watermark.controller";
|
|
16
17
|
import { hexToRGB } from "../helpers/utils";
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -218,3 +219,28 @@ export function* makeElementTrack({
|
|
|
218
219
|
yield* all(...sequence);
|
|
219
220
|
yield elementTrackRef().remove();
|
|
220
221
|
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Creates a watermark overlay on top of all tracks.
|
|
225
|
+
* Dispatches to the registered watermark renderer by type (text | image).
|
|
226
|
+
* Added last to ensure it renders on top of all content.
|
|
227
|
+
*/
|
|
228
|
+
export function* makeWatermarkTrack({
|
|
229
|
+
view,
|
|
230
|
+
watermark,
|
|
231
|
+
duration,
|
|
232
|
+
}: {
|
|
233
|
+
view: View2D;
|
|
234
|
+
watermark: WatermarkInput;
|
|
235
|
+
duration: number;
|
|
236
|
+
}) {
|
|
237
|
+
if (duration <= 0) return;
|
|
238
|
+
|
|
239
|
+
// Add watermark after other tracks have started (ensures it renders on top)
|
|
240
|
+
yield* waitFor(0.001);
|
|
241
|
+
|
|
242
|
+
const renderer = watermarkController.get(watermark.type);
|
|
243
|
+
if (renderer) {
|
|
244
|
+
yield* renderer.render({ view, watermark, duration });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AudioElement } from "../elements/audio.element";
|
|
2
2
|
import { CaptionElement } from "../elements/caption.element";
|
|
3
3
|
import { CircleElement } from "../elements/circle.element";
|
|
4
|
-
import { IconElement } from "../elements/icon.element";
|
|
5
4
|
import { ImageElement } from "../elements/image.element";
|
|
6
5
|
import { RectElement } from "../elements/rect.element";
|
|
7
6
|
import { SceneElement } from "../elements/scene.element";
|
|
@@ -33,7 +32,6 @@ export class ElementController {
|
|
|
33
32
|
elementController.register(TextElement);
|
|
34
33
|
elementController.register(AudioElement);
|
|
35
34
|
elementController.register(CircleElement);
|
|
36
|
-
elementController.register(IconElement);
|
|
37
35
|
elementController.register(RectElement);
|
|
38
36
|
}
|
|
39
37
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { WatermarkRendererContract } from "../helpers/watermark.types";
|
|
2
|
+
import { TextWatermarkRenderer } from "../watermark-renderers/text.watermark";
|
|
3
|
+
import { ImageWatermarkRenderer } from "../watermark-renderers/image.watermark";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Registry for watermark renderers. Enables scalable dispatch by type:
|
|
7
|
+
* watermarkController.get(watermark.type)?.render({ view, watermark, duration }).
|
|
8
|
+
* Add new watermark types (e.g. svg) by registering a new renderer.
|
|
9
|
+
*/
|
|
10
|
+
export class WatermarkController {
|
|
11
|
+
private renderers = new Map<string, WatermarkRendererContract>();
|
|
12
|
+
|
|
13
|
+
register(renderer: WatermarkRendererContract) {
|
|
14
|
+
this.renderers.set(renderer.name, renderer);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get(name: string): WatermarkRendererContract | undefined {
|
|
18
|
+
return this.renderers.get(name);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
list(): string[] {
|
|
22
|
+
return Array.from(this.renderers.keys());
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const watermarkController = new WatermarkController();
|
|
27
|
+
|
|
28
|
+
function registerWatermarkRenderers() {
|
|
29
|
+
watermarkController.register(TextWatermarkRenderer);
|
|
30
|
+
watermarkController.register(ImageWatermarkRenderer);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
registerWatermarkRenderers();
|
|
34
|
+
|
|
35
|
+
export default watermarkController;
|
package/src/elements/index.ts
CHANGED
|
@@ -10,5 +10,4 @@ export { TextElement } from './text.element';
|
|
|
10
10
|
export { CaptionElement } from './caption.element';
|
|
11
11
|
export { RectElement } from './rect.element';
|
|
12
12
|
export { CircleElement } from './circle.element';
|
|
13
|
-
export { IconElement } from './icon.element';
|
|
14
13
|
export { SceneElement } from './scene.element';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ElementParams } from "../helpers/types";
|
|
2
|
-
import { all, createRef, waitFor } from "@twick/core";
|
|
3
|
-
import { Txt } from "@twick/2d";
|
|
2
|
+
import { all, Color, createRef, waitFor } from "@twick/core";
|
|
3
|
+
import { Rect, Txt } from "@twick/2d";
|
|
4
4
|
import { addAnimation, addTextEffect } from "../helpers/element.utils";
|
|
5
|
-
import {
|
|
5
|
+
import { hexToRGB } from "../helpers/utils";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @group TextElement
|
|
@@ -79,26 +79,85 @@ export const TextElement = {
|
|
|
79
79
|
* });
|
|
80
80
|
* ```
|
|
81
81
|
*/
|
|
82
|
-
*create({ containerRef, element, view }: ElementParams) {
|
|
82
|
+
*create({ containerRef, element, view }: ElementParams) {
|
|
83
83
|
const elementRef = createRef<any>();
|
|
84
|
+
const hasBackground =
|
|
85
|
+
element.props?.backgroundColor != null &&
|
|
86
|
+
element.props.backgroundColor !== "";
|
|
84
87
|
|
|
85
88
|
yield* waitFor(element?.s);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
89
|
+
|
|
90
|
+
if (hasBackground) {
|
|
91
|
+
const textRef = createRef<Txt>();
|
|
92
|
+
const wrapperRef = createRef<any>();
|
|
93
|
+
const innerTextRef = createRef<Txt>();
|
|
94
|
+
|
|
95
|
+
yield containerRef().add(
|
|
96
|
+
<Txt
|
|
97
|
+
ref={textRef}
|
|
98
|
+
key={`${element.id}-measure`}
|
|
99
|
+
text={element.t}
|
|
100
|
+
textWrap={element.props?.textWrap ?? true}
|
|
101
|
+
textAlign={element.props?.textAlign ?? "center"}
|
|
102
|
+
opacity={0}
|
|
103
|
+
{...element.props}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const bgColor = new Color({
|
|
108
|
+
...hexToRGB(element.props!.backgroundColor!),
|
|
109
|
+
a: element.props?.backgroundOpacity ?? 1,
|
|
110
|
+
});
|
|
111
|
+
const paddingX = 20;
|
|
112
|
+
const paddingY = 4;
|
|
113
|
+
|
|
114
|
+
yield containerRef().add(
|
|
115
|
+
<Rect
|
|
116
|
+
ref={wrapperRef}
|
|
117
|
+
key={element.id}
|
|
118
|
+
fill={bgColor}
|
|
119
|
+
width={textRef().width() + paddingX}
|
|
120
|
+
height={textRef().height() + paddingY}
|
|
121
|
+
padding={[0, paddingX / 2]}
|
|
122
|
+
alignItems={"center"}
|
|
123
|
+
justifyContent={"center"}
|
|
124
|
+
layout
|
|
125
|
+
>
|
|
126
|
+
<Txt
|
|
127
|
+
ref={innerTextRef}
|
|
128
|
+
text={element.t}
|
|
129
|
+
textWrap={element.props?.textWrap ?? true}
|
|
130
|
+
textAlign={element.props?.textAlign ?? "center"}
|
|
131
|
+
{...element.props}
|
|
132
|
+
/>
|
|
133
|
+
</Rect>
|
|
134
|
+
);
|
|
135
|
+
textRef().remove();
|
|
136
|
+
|
|
137
|
+
yield* all(
|
|
138
|
+
addAnimation({ elementRef: wrapperRef, element: element, view }),
|
|
139
|
+
addTextEffect({ elementRef: innerTextRef, element: element }),
|
|
140
|
+
waitFor(Math.max(0, element.e - element.s))
|
|
141
|
+
);
|
|
142
|
+
yield wrapperRef().remove();
|
|
143
|
+
} else {
|
|
144
|
+
yield containerRef().add(
|
|
145
|
+
<Txt
|
|
146
|
+
ref={elementRef}
|
|
147
|
+
key={element.id}
|
|
148
|
+
text={element.t}
|
|
149
|
+
textWrap={element.props?.textWrap ?? true}
|
|
150
|
+
textAlign={element.props?.textAlign ?? "center"}
|
|
151
|
+
{...element.props}
|
|
152
|
+
/>
|
|
153
|
+
);
|
|
154
|
+
yield* all(
|
|
155
|
+
addAnimation({ elementRef: elementRef, element: element, view }),
|
|
156
|
+
addTextEffect({ elementRef: elementRef, element: element }),
|
|
157
|
+
waitFor(Math.max(0, element.e - element.s))
|
|
158
|
+
);
|
|
159
|
+
yield elementRef().remove();
|
|
160
|
+
}
|
|
161
|
+
},
|
|
103
162
|
}
|
|
104
163
|
|
package/src/helpers/types.ts
CHANGED
|
@@ -1,19 +1,48 @@
|
|
|
1
1
|
import { View2D } from "@twick/2d";
|
|
2
2
|
import { Reference, ThreadGenerator, Vector2 } from "@twick/core";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Watermark configuration for overlay on video.
|
|
6
|
+
* Supports text or image watermarks with position, rotation, and opacity.
|
|
7
|
+
* Compatible with WatermarkJSON from @twick/timeline.
|
|
8
|
+
*/
|
|
9
|
+
export type WatermarkInput = {
|
|
10
|
+
type: "text" | "image";
|
|
11
|
+
position?: Position;
|
|
12
|
+
rotation?: number;
|
|
13
|
+
opacity?: number;
|
|
14
|
+
props: {
|
|
15
|
+
text?: string;
|
|
16
|
+
fontSize?: number;
|
|
17
|
+
fontFamily?: string;
|
|
18
|
+
fill?: string;
|
|
19
|
+
stroke?: string;
|
|
20
|
+
strokeWidth?: number;
|
|
21
|
+
textAlign?: string;
|
|
22
|
+
fontWeight?: number;
|
|
23
|
+
lineWidth?: number;
|
|
24
|
+
fontStyle?: string;
|
|
25
|
+
src?: string;
|
|
26
|
+
width?: number;
|
|
27
|
+
height?: number;
|
|
28
|
+
objectFit?: ObjectFit;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
4
32
|
/**
|
|
5
33
|
* Main input configuration for video visualization.
|
|
6
|
-
* Contains player settings, background color, dimensions,
|
|
7
|
-
* for creating complete video visualizations.
|
|
34
|
+
* Contains player settings, background color, dimensions, track definitions,
|
|
35
|
+
* and optional watermark for creating complete video visualizations.
|
|
8
36
|
*/
|
|
9
37
|
export type VideoInput = {
|
|
10
|
-
playerId: string
|
|
38
|
+
playerId: string;
|
|
11
39
|
backgroundColor: string;
|
|
12
40
|
properties: {
|
|
13
41
|
width: number;
|
|
14
42
|
height: number;
|
|
15
43
|
};
|
|
16
44
|
tracks: VisualizerTrack[];
|
|
45
|
+
watermark?: WatermarkInput;
|
|
17
46
|
};
|
|
18
47
|
|
|
19
48
|
/**
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { View2D } from "@twick/2d";
|
|
2
|
+
import { ThreadGenerator } from "@twick/core";
|
|
3
|
+
import { WatermarkInput } from "./types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parameters passed to a watermark renderer.
|
|
7
|
+
*/
|
|
8
|
+
export type WatermarkRendererParams = {
|
|
9
|
+
view: View2D;
|
|
10
|
+
watermark: WatermarkInput;
|
|
11
|
+
duration: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Interface for watermark renderers. Register with WatermarkController
|
|
16
|
+
* to support text, image, or future watermark types (e.g. svg).
|
|
17
|
+
*/
|
|
18
|
+
export interface WatermarkRendererContract {
|
|
19
|
+
/** Renderer type: "text" | "image" */
|
|
20
|
+
name: "text" | "image";
|
|
21
|
+
/** Renders the watermark overlay for the given duration */
|
|
22
|
+
render(params: WatermarkRendererParams): ThreadGenerator;
|
|
23
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -194,3 +194,8 @@ export * from './elements';
|
|
|
194
194
|
export * from './text-effects';
|
|
195
195
|
export * from './frame-effects';
|
|
196
196
|
|
|
197
|
+
// Watermark renderer registry (Option B – register custom watermark types)
|
|
198
|
+
export { default as watermarkController } from './controllers/watermark.controller';
|
|
199
|
+
export { WatermarkController } from './controllers/watermark.controller';
|
|
200
|
+
export type { WatermarkRendererContract, WatermarkRendererParams } from './helpers/watermark.types';
|
|
201
|
+
|
package/src/sample.ts
CHANGED
|
@@ -26,6 +26,18 @@ export const sample = {
|
|
|
26
26
|
"orgId": "a251d9971a55"
|
|
27
27
|
},
|
|
28
28
|
"basePath": "carlyn",
|
|
29
|
+
"watermark": {
|
|
30
|
+
"id": "e-watermark",
|
|
31
|
+
"type": "text",
|
|
32
|
+
"position": { "x": 0, "y": -200 },
|
|
33
|
+
"opacity": 0.7,
|
|
34
|
+
"props": {
|
|
35
|
+
"text": "© Twick SDK",
|
|
36
|
+
"fontSize": 24,
|
|
37
|
+
"fontFamily": "Arial",
|
|
38
|
+
"fill": "#ffffff"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
29
41
|
"tracks": [
|
|
30
42
|
{
|
|
31
43
|
"id": "t-scene",
|
|
@@ -55,7 +55,6 @@ export { CaptionElement } from './elements/caption.element';
|
|
|
55
55
|
// Shape and UI elements
|
|
56
56
|
export { RectElement } from './elements/rect.element';
|
|
57
57
|
export { CircleElement } from './elements/circle.element';
|
|
58
|
-
export { IconElement } from './elements/icon.element';
|
|
59
58
|
|
|
60
59
|
// Special elements
|
|
61
60
|
export { SceneElement } from './elements/scene.element';
|
package/src/visualizer.tsx
CHANGED
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
makeElementTrack,
|
|
58
58
|
makeSceneTrack,
|
|
59
59
|
makeVideoTrack,
|
|
60
|
+
makeWatermarkTrack,
|
|
60
61
|
} from "./components/track";
|
|
61
62
|
import { dispatchWindowEvent } from "./helpers/event.utils";
|
|
62
63
|
|
|
@@ -133,7 +134,7 @@ export const scene = makeScene2D("scene", function* (view: View2D) {
|
|
|
133
134
|
const movie = [];
|
|
134
135
|
let index = 1;
|
|
135
136
|
|
|
136
|
-
// Iterate through each track
|
|
137
|
+
// Iterate through each track. Z-order is determined by track order (first track = back, last = front).
|
|
137
138
|
for (const track of input.tracks) {
|
|
138
139
|
switch (track.type) {
|
|
139
140
|
case TRACK_TYPES.VIDEO:
|
|
@@ -159,6 +160,25 @@ export const scene = makeScene2D("scene", function* (view: View2D) {
|
|
|
159
160
|
}
|
|
160
161
|
index++;
|
|
161
162
|
}
|
|
163
|
+
|
|
164
|
+
// Add watermark on top of all tracks when present
|
|
165
|
+
if (input.watermark) {
|
|
166
|
+
const duration =
|
|
167
|
+
input.tracks?.length > 0
|
|
168
|
+
? Math.max(
|
|
169
|
+
0,
|
|
170
|
+
...input.tracks.flatMap((t) => t.elements || []).map((e) => e.e)
|
|
171
|
+
)
|
|
172
|
+
: 10;
|
|
173
|
+
movie.push(
|
|
174
|
+
makeWatermarkTrack({
|
|
175
|
+
view,
|
|
176
|
+
watermark: input.watermark,
|
|
177
|
+
duration,
|
|
178
|
+
})
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
162
182
|
// Execute all track animations in parallel
|
|
163
183
|
yield* all(...movie);
|
|
164
184
|
dispatchWindowEvent(EVENT_TYPES.PLAYER_UPDATE, {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image watermark renderer for the visualizer.
|
|
3
|
+
* Renders image overlay with position, rotation, opacity, and dimensions.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Img } from "@twick/2d";
|
|
7
|
+
import { createRef, waitFor } from "@twick/core";
|
|
8
|
+
import type { WatermarkRendererContract } from "../helpers/watermark.types";
|
|
9
|
+
|
|
10
|
+
export const ImageWatermarkRenderer: WatermarkRendererContract = {
|
|
11
|
+
name: "image",
|
|
12
|
+
|
|
13
|
+
*render({ view, watermark, duration }) {
|
|
14
|
+
const props = watermark.props;
|
|
15
|
+
if (!props?.src) return;
|
|
16
|
+
|
|
17
|
+
const position = watermark.position ?? { x: 0, y: 0 };
|
|
18
|
+
const rotation = (watermark.rotation ?? 0) * (Math.PI / 180);
|
|
19
|
+
const opacity = watermark.opacity ?? 1;
|
|
20
|
+
|
|
21
|
+
const watermarkRef = createRef<any>();
|
|
22
|
+
view.add(
|
|
23
|
+
<Img
|
|
24
|
+
ref={watermarkRef}
|
|
25
|
+
src={props.src}
|
|
26
|
+
x={position.x}
|
|
27
|
+
y={position.y}
|
|
28
|
+
width={props.width}
|
|
29
|
+
height={props.height}
|
|
30
|
+
rotation={rotation}
|
|
31
|
+
opacity={opacity}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
yield* waitFor(duration);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text watermark renderer for the visualizer.
|
|
3
|
+
* Renders text overlay with position, rotation, opacity, and text styling.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Txt } from "@twick/2d";
|
|
7
|
+
import { createRef, waitFor } from "@twick/core";
|
|
8
|
+
import type { WatermarkRendererContract } from "../helpers/watermark.types";
|
|
9
|
+
|
|
10
|
+
export const TextWatermarkRenderer: WatermarkRendererContract = {
|
|
11
|
+
name: "text",
|
|
12
|
+
|
|
13
|
+
*render({ view, watermark, duration }) {
|
|
14
|
+
const props = watermark.props;
|
|
15
|
+
if (!props?.text) return;
|
|
16
|
+
|
|
17
|
+
const position = watermark.position ?? { x: 0, y: 0 };
|
|
18
|
+
const rotation = (watermark.rotation ?? 0) * (Math.PI / 180);
|
|
19
|
+
const opacity = watermark.opacity ?? 1;
|
|
20
|
+
|
|
21
|
+
const watermarkRef = createRef<any>();
|
|
22
|
+
view.add(
|
|
23
|
+
<Txt
|
|
24
|
+
ref={watermarkRef}
|
|
25
|
+
text={props.text}
|
|
26
|
+
x={position.x}
|
|
27
|
+
y={position.y}
|
|
28
|
+
rotation={rotation}
|
|
29
|
+
opacity={opacity}
|
|
30
|
+
fontSize={props.fontSize ?? 24}
|
|
31
|
+
fontFamily={props.fontFamily ?? "Arial"}
|
|
32
|
+
fill={props.fill ?? "#ffffff"}
|
|
33
|
+
stroke={props.stroke}
|
|
34
|
+
lineWidth={props.lineWidth ?? props.strokeWidth ?? 0}
|
|
35
|
+
textAlign={(props.textAlign as any) ?? "center"}
|
|
36
|
+
fontWeight={props.fontWeight ?? 400}
|
|
37
|
+
fontStyle={props.fontStyle ?? "normal"}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
yield* waitFor(duration);
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -1,88 +0,0 @@
|
|
|
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
|
-
/**
|
|
7
|
-
* @group IconElement
|
|
8
|
-
* IconElement creates and manages icon elements in the visualizer scene.
|
|
9
|
-
* Handles icon rendering, styling, and animations for UI elements and
|
|
10
|
-
* visual design components.
|
|
11
|
-
*
|
|
12
|
-
* Features:
|
|
13
|
-
* - Icon rendering with custom styling
|
|
14
|
-
* - Size and position control
|
|
15
|
-
* - Color and opacity customization
|
|
16
|
-
* - Animation support
|
|
17
|
-
*
|
|
18
|
-
* @param containerRef - Reference to the container element
|
|
19
|
-
* @param element - Icon element configuration and properties
|
|
20
|
-
* @param view - The main scene view for rendering
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```js
|
|
24
|
-
* // Basic icon element
|
|
25
|
-
* {
|
|
26
|
-
* id: "play-icon",
|
|
27
|
-
* type: "icon",
|
|
28
|
-
* s: 0,
|
|
29
|
-
* e: 20,
|
|
30
|
-
* props: {
|
|
31
|
-
* icon: "play",
|
|
32
|
-
* size: 64,
|
|
33
|
-
* fill: "#ffffff"
|
|
34
|
-
* }
|
|
35
|
-
* }
|
|
36
|
-
*
|
|
37
|
-
* // Icon with animation
|
|
38
|
-
* {
|
|
39
|
-
* id: "animated-icon",
|
|
40
|
-
* type: "icon",
|
|
41
|
-
* s: 2,
|
|
42
|
-
* e: 15,
|
|
43
|
-
* props: {
|
|
44
|
-
* icon: "pause",
|
|
45
|
-
* size: 48,
|
|
46
|
-
* fill: "#ff0000",
|
|
47
|
-
* opacity: 0.8
|
|
48
|
-
* },
|
|
49
|
-
* animation: {
|
|
50
|
-
* name: "fade",
|
|
51
|
-
* animate: "enter",
|
|
52
|
-
* duration: 2
|
|
53
|
-
* }
|
|
54
|
-
* }
|
|
55
|
-
* ```
|
|
56
|
-
*/
|
|
57
|
-
export const IconElement = {
|
|
58
|
-
name: "icon",
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Generator function that creates and manages icon elements in the scene.
|
|
62
|
-
* Handles icon creation, styling, and cleanup.
|
|
63
|
-
*
|
|
64
|
-
* @param params - Element parameters including container reference, element config, and view
|
|
65
|
-
* @returns Generator that controls the icon element lifecycle
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```js
|
|
69
|
-
* yield* IconElement.create({
|
|
70
|
-
* containerRef: mainContainer,
|
|
71
|
-
* element: iconConfig,
|
|
72
|
-
* view: sceneView
|
|
73
|
-
* });
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
|
-
*create({ containerRef, element, view }: ElementParams) {
|
|
77
|
-
const elementRef = createRef<any>();
|
|
78
|
-
yield* waitFor(element?.s);
|
|
79
|
-
yield containerRef().add(
|
|
80
|
-
<Icon ref={elementRef} key={element.id} {...element.props} />
|
|
81
|
-
);
|
|
82
|
-
yield* all(
|
|
83
|
-
addAnimation({ elementRef: elementRef, element: element, view }),
|
|
84
|
-
waitFor(Math.max(0, element.e - element.s))
|
|
85
|
-
);
|
|
86
|
-
yield elementRef().remove();
|
|
87
|
-
},
|
|
88
|
-
};
|