@twick/visualizer 0.14.0 → 0.14.3
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 +113 -13
- package/package.json +14 -14
- package/package.json.bak +34 -0
- package/src/animations/blur.tsx +96 -60
- package/src/animations/breathe.tsx +95 -60
- package/src/animations/fade.tsx +173 -60
- package/src/animations/index.ts +12 -0
- package/src/animations/photo-rise.tsx +103 -66
- package/src/animations/photo-zoom.tsx +109 -73
- package/src/animations/rise.tsx +157 -118
- package/src/animations/succession.tsx +112 -77
- package/src/components/frame-effects.tsx +188 -188
- package/src/components/track.tsx +237 -232
- package/src/controllers/animation.controller.ts +38 -38
- package/src/controllers/element.controller.ts +42 -42
- package/src/controllers/frame-effect.controller.tsx +29 -29
- package/src/controllers/text-effect.controller.ts +32 -32
- package/src/elements/audio.element.tsx +79 -17
- package/src/elements/caption.element.tsx +169 -87
- package/src/elements/circle.element.tsx +88 -20
- package/src/elements/icon.element.tsx +88 -20
- package/src/elements/image.element.tsx +134 -55
- package/src/elements/index.ts +14 -0
- package/src/elements/rect.element.tsx +92 -22
- package/src/elements/scene.element.tsx +97 -29
- package/src/elements/text.element.tsx +101 -27
- package/src/elements/video.element.tsx +274 -56
- package/src/frame-effects/circle.frame.tsx +168 -103
- package/src/frame-effects/index.ts +7 -0
- package/src/frame-effects/rect.frame.tsx +198 -103
- package/src/global.css +11 -11
- package/src/helpers/caption.utils.ts +29 -29
- package/src/helpers/constants.ts +162 -158
- package/src/helpers/element.utils.ts +331 -239
- package/src/helpers/event.utils.ts +21 -0
- package/src/helpers/filters.ts +127 -127
- package/src/helpers/log.utils.ts +55 -29
- package/src/helpers/timing.utils.ts +109 -109
- package/src/helpers/types.ts +361 -241
- package/src/helpers/utils.ts +36 -19
- package/src/index.ts +196 -6
- package/src/live.tsx +16 -16
- package/src/project.ts +6 -6
- package/src/sample.ts +247 -247
- package/src/text-effects/elastic.tsx +70 -39
- package/src/text-effects/erase.tsx +91 -58
- package/src/text-effects/index.ts +9 -0
- package/src/text-effects/stream-word.tsx +94 -60
- package/src/text-effects/typewriter.tsx +93 -59
- package/src/visualizer-grouped.ts +83 -0
- package/src/visualizer.tsx +182 -78
- package/tsconfig.json +11 -11
- package/typedoc.json +19 -14
- package/vite.config.ts +15 -15
- package/.turbo/turbo-build.log +0 -19
- package/.turbo/turbo-docs.log +0 -7
- package/LICENSE +0 -197
- package/dist/mp4.wasm +0 -0
- package/dist/project.css +0 -1
- package/dist/project.js +0 -145
- package/docs/.nojekyll +0 -1
- package/docs/README.md +0 -13
- package/docs/interfaces/Animation.md +0 -47
- package/docs/interfaces/Element.md +0 -47
- package/docs/interfaces/FrameEffectPlugin.md +0 -47
- package/docs/interfaces/TextEffect.md +0 -47
- package/docs/modules.md +0 -535
|
@@ -1,56 +1,274 @@
|
|
|
1
|
-
import { ElementParams } from "../helpers/types";
|
|
2
|
-
import { all, createRef, waitFor } from "@twick/core";
|
|
3
|
-
import {
|
|
4
|
-
import { addAnimation, addFrameEffect, fitElement } from "../helpers/element.utils";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
1
|
+
import { ElementParams } from "../helpers/types";
|
|
2
|
+
import { all, createRef, waitFor } from "@twick/core";
|
|
3
|
+
import { Video, Rect } from "@twick/2d";
|
|
4
|
+
import { addAnimation, addFrameEffect, fitElement } from "../helpers/element.utils";
|
|
5
|
+
import { applyColorFilter } from "../helpers/filters";
|
|
6
|
+
import { logger } from "../helpers/log.utils";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @group VideoElement
|
|
10
|
+
* @description Professional video content management with effects and animations
|
|
11
|
+
*
|
|
12
|
+
* VideoElement creates and manages video content in the visualizer scene.
|
|
13
|
+
* Handles video playback, frame effects, animations, and content fitting
|
|
14
|
+
* for professional video presentations and content creation.
|
|
15
|
+
*
|
|
16
|
+
* ## Key Features
|
|
17
|
+
*
|
|
18
|
+
* - **Video playback** with start/end timing control
|
|
19
|
+
* - **Frame effects** and animations for visual enhancement
|
|
20
|
+
* - **Object fit options** for content scaling (contain, cover, fill, none)
|
|
21
|
+
* - **Color filters** and media effects for artistic styling
|
|
22
|
+
* - **Automatic cleanup** and resource management
|
|
23
|
+
* - **Synchronization** with other scene elements
|
|
24
|
+
*
|
|
25
|
+
* ## Use Cases
|
|
26
|
+
*
|
|
27
|
+
* - **Main content videos**: Primary video content with effects
|
|
28
|
+
* - **Background videos**: Ambient video backgrounds
|
|
29
|
+
* - **Video overlays**: Picture-in-picture style content
|
|
30
|
+
* - **Video transitions**: Smooth scene-to-scene transitions
|
|
31
|
+
* - **Video effects**: Artistic and creative video manipulation
|
|
32
|
+
*
|
|
33
|
+
* ## Best Practices
|
|
34
|
+
*
|
|
35
|
+
* - **Format**: Use MP4 with H.264 encoding for best compatibility
|
|
36
|
+
* - **Resolution**: Match scene dimensions or use appropriate object-fit
|
|
37
|
+
* - **Performance**: Optimize video files for web delivery
|
|
38
|
+
* - **Timing**: Set precise start/end times for synchronization
|
|
39
|
+
* - **Effects**: Combine with animations and frame effects for impact
|
|
40
|
+
*
|
|
41
|
+
* ## Integration Examples
|
|
42
|
+
*
|
|
43
|
+
* ### Basic Video Element
|
|
44
|
+
* ```js
|
|
45
|
+
* {
|
|
46
|
+
* id: "main-video",
|
|
47
|
+
* type: "video",
|
|
48
|
+
* s: 0, e: 15,
|
|
49
|
+
* props: {
|
|
50
|
+
* src: "video.mp4",
|
|
51
|
+
* width: 1920,
|
|
52
|
+
* height: 1080
|
|
53
|
+
* },
|
|
54
|
+
* objectFit: "cover"
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* ### Video with Animation
|
|
59
|
+
* ```js
|
|
60
|
+
* {
|
|
61
|
+
* id: "intro-video",
|
|
62
|
+
* type: "video",
|
|
63
|
+
* s: 0, e: 10,
|
|
64
|
+
* props: { src: "intro.mp4" },
|
|
65
|
+
* animation: {
|
|
66
|
+
* name: "fade",
|
|
67
|
+
* animate: "enter",
|
|
68
|
+
* duration: 2
|
|
69
|
+
* },
|
|
70
|
+
* objectFit: "contain"
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* ### Video with Frame Effect
|
|
75
|
+
* ```js
|
|
76
|
+
* {
|
|
77
|
+
* id: "framed-video",
|
|
78
|
+
* type: "video",
|
|
79
|
+
* s: 2, e: 20,
|
|
80
|
+
* props: { src: "content.mp4" },
|
|
81
|
+
* frameEffects: [{
|
|
82
|
+
* name: "circle",
|
|
83
|
+
* s: 2, e: 20,
|
|
84
|
+
* props: {
|
|
85
|
+
* frameSize: [500, 500],
|
|
86
|
+
* frameShape: "circle",
|
|
87
|
+
* framePosition: { x: 960, y: 540 },
|
|
88
|
+
* radius: 250,
|
|
89
|
+
* objectFit: "cover",
|
|
90
|
+
* transitionDuration: 1.5
|
|
91
|
+
* }
|
|
92
|
+
* }]
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* ### Complex Video Scene
|
|
97
|
+
* ```js
|
|
98
|
+
* // Multi-track video scene with overlays
|
|
99
|
+
* const videoScene = {
|
|
100
|
+
* backgroundColor: "#000000",
|
|
101
|
+
* playerId: "video-player",
|
|
102
|
+
* properties: { width: 1920, height: 1080 },
|
|
103
|
+
* tracks: [
|
|
104
|
+
* {
|
|
105
|
+
* id: "background",
|
|
106
|
+
* type: "video",
|
|
107
|
+
* elements: [{
|
|
108
|
+
* id: "bg-video",
|
|
109
|
+
* type: "video",
|
|
110
|
+
* s: 0, e: 30,
|
|
111
|
+
* props: { src: "background.mp4", opacity: 0.3 },
|
|
112
|
+
* objectFit: "cover"
|
|
113
|
+
* }]
|
|
114
|
+
* },
|
|
115
|
+
* {
|
|
116
|
+
* id: "main-content",
|
|
117
|
+
* type: "video",
|
|
118
|
+
* elements: [
|
|
119
|
+
* {
|
|
120
|
+
* id: "main-video",
|
|
121
|
+
* type: "video",
|
|
122
|
+
* s: 2, e: 25,
|
|
123
|
+
* props: { src: "main-content.mp4" },
|
|
124
|
+
* animation: {
|
|
125
|
+
* name: "fade",
|
|
126
|
+
* animate: "enter",
|
|
127
|
+
* duration: 2
|
|
128
|
+
* },
|
|
129
|
+
* frameEffects: [{
|
|
130
|
+
* name: "rect",
|
|
131
|
+
* s: 2, e: 25,
|
|
132
|
+
* props: {
|
|
133
|
+
* frameSize: [800, 600],
|
|
134
|
+
* frameShape: "rect",
|
|
135
|
+
* framePosition: { x: 960, y: 540 },
|
|
136
|
+
* radius: 20,
|
|
137
|
+
* objectFit: "cover"
|
|
138
|
+
* }
|
|
139
|
+
* }]
|
|
140
|
+
* },
|
|
141
|
+
* {
|
|
142
|
+
* id: "video-caption",
|
|
143
|
+
* type: "caption",
|
|
144
|
+
* s: 5, e: 20,
|
|
145
|
+
* t: "Video Caption",
|
|
146
|
+
* props: {
|
|
147
|
+
* colors: { text: "#ffffff", background: "rgba(0,0,0,0.7)" },
|
|
148
|
+
* font: { family: "Arial", size: 36, weight: 600 }
|
|
149
|
+
* }
|
|
150
|
+
* }
|
|
151
|
+
* ]
|
|
152
|
+
* }
|
|
153
|
+
* ]
|
|
154
|
+
* };
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* ## 🚀 Performance Tips
|
|
158
|
+
*
|
|
159
|
+
* - **Preload videos** for smooth playback
|
|
160
|
+
* - **Use appropriate object-fit** to avoid unnecessary scaling
|
|
161
|
+
* - **Optimize video files** for web delivery (compression, format)
|
|
162
|
+
* - **Batch frame effects** for better performance
|
|
163
|
+
* - **Monitor memory usage** with multiple video elements
|
|
164
|
+
*
|
|
165
|
+
* @param containerRef - Reference to the container element
|
|
166
|
+
* @param element - Video element configuration and properties
|
|
167
|
+
* @param view - The main scene view for rendering
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```js
|
|
171
|
+
* // Basic video element
|
|
172
|
+
* {
|
|
173
|
+
* id: "main-video",
|
|
174
|
+
* type: "video",
|
|
175
|
+
* s: 0, e: 15,
|
|
176
|
+
* props: {
|
|
177
|
+
* src: "video.mp4",
|
|
178
|
+
* width: 1920,
|
|
179
|
+
* height: 1080
|
|
180
|
+
* },
|
|
181
|
+
* objectFit: "cover"
|
|
182
|
+
* }
|
|
183
|
+
*
|
|
184
|
+
* // Video with frame effect and animation
|
|
185
|
+
* {
|
|
186
|
+
* id: "framed-video",
|
|
187
|
+
* type: "video",
|
|
188
|
+
* s: 2, e: 20,
|
|
189
|
+
* props: { src: "content.mp4" },
|
|
190
|
+
* animation: {
|
|
191
|
+
* name: "fade",
|
|
192
|
+
* animate: "enter",
|
|
193
|
+
* duration: 2
|
|
194
|
+
* },
|
|
195
|
+
* frameEffects: [{
|
|
196
|
+
* name: "circle",
|
|
197
|
+
* s: 2, e: 20,
|
|
198
|
+
* props: {
|
|
199
|
+
* frameSize: [500, 500],
|
|
200
|
+
* frameShape: "circle",
|
|
201
|
+
* framePosition: { x: 960, y: 540 },
|
|
202
|
+
* radius: 250,
|
|
203
|
+
* objectFit: "cover"
|
|
204
|
+
* }
|
|
205
|
+
* }]
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
export const VideoElement = {
|
|
210
|
+
name: "video",
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Generator function that creates and manages video elements in the scene.
|
|
214
|
+
* Handles video creation, frame setup, animations, effects, and cleanup.
|
|
215
|
+
*
|
|
216
|
+
* @param params - Element parameters including container reference, element config, and view
|
|
217
|
+
* @returns Generator that controls the video element lifecycle
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```js
|
|
221
|
+
* yield* VideoElement.create({
|
|
222
|
+
* containerRef: mainContainer,
|
|
223
|
+
* element: videoConfig,
|
|
224
|
+
* view: sceneView
|
|
225
|
+
* });
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
*create({ containerRef, element, view }: ElementParams) {
|
|
229
|
+
yield* waitFor(element?.s);
|
|
230
|
+
const frameContainerRef = createRef<any>();
|
|
231
|
+
const frameElementRef = createRef<any>();
|
|
232
|
+
|
|
233
|
+
yield containerRef().add(
|
|
234
|
+
<Rect ref={frameContainerRef} key={element.id} {...element.frame}>
|
|
235
|
+
<Video
|
|
236
|
+
ref={frameElementRef}
|
|
237
|
+
key={`child-${element.id}`}
|
|
238
|
+
play={true}
|
|
239
|
+
{...element.props}
|
|
240
|
+
/>
|
|
241
|
+
</Rect>
|
|
242
|
+
);
|
|
243
|
+
if (frameContainerRef()) {
|
|
244
|
+
yield fitElement({
|
|
245
|
+
elementRef: frameElementRef,
|
|
246
|
+
containerSize: frameContainerRef().size(),
|
|
247
|
+
elementSize: frameElementRef().size(),
|
|
248
|
+
objectFit: element.objectFit,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
if (element?.props?.mediaFilter) {
|
|
252
|
+
applyColorFilter(frameElementRef, element.props.mediaFilter);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
yield* all(
|
|
256
|
+
addAnimation({
|
|
257
|
+
elementRef: frameElementRef,
|
|
258
|
+
containerRef: frameContainerRef,
|
|
259
|
+
element: element,
|
|
260
|
+
view,
|
|
261
|
+
}),
|
|
262
|
+
addFrameEffect({
|
|
263
|
+
containerRef: frameContainerRef,
|
|
264
|
+
elementRef: frameElementRef,
|
|
265
|
+
element,
|
|
266
|
+
}),
|
|
267
|
+
waitFor(Math.max(0, element.e - element.s))
|
|
268
|
+
);
|
|
269
|
+
yield frameElementRef().play(false);
|
|
270
|
+
yield frameElementRef().remove();
|
|
271
|
+
yield frameContainerRef().remove();
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
};
|
|
@@ -1,103 +1,168 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @group CircleFrameEffect
|
|
3
|
+
* CircleFrameEffect animates a frame transitioning into a circular shape,
|
|
4
|
+
* resizing, repositioning, and optionally fitting its content. Creates a
|
|
5
|
+
* circular mask around content perfect for profile pictures and artistic presentations.
|
|
6
|
+
*
|
|
7
|
+
* Behavior:
|
|
8
|
+
* - Waits for the specified start time
|
|
9
|
+
* - Resizes and repositions the frame container smoothly
|
|
10
|
+
* - Animates the corner radius to create a perfect circle
|
|
11
|
+
* - Repositions the content element if needed
|
|
12
|
+
* - Optionally fits the element inside the container based on object-fit
|
|
13
|
+
*
|
|
14
|
+
* @param containerRef - Reference to the frame container element
|
|
15
|
+
* @param elementRef - Reference to the content element inside the frame
|
|
16
|
+
* @param initFrameState - Initial size and position state of the element
|
|
17
|
+
* @param frameEffect - Frame transformation configuration and timing
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```js
|
|
21
|
+
* // Basic circular frame
|
|
22
|
+
* frameEffects: [{
|
|
23
|
+
* name: "circle",
|
|
24
|
+
* s: 0,
|
|
25
|
+
* e: 10,
|
|
26
|
+
* props: {
|
|
27
|
+
* frameSize: [400, 400],
|
|
28
|
+
* frameShape: "circle",
|
|
29
|
+
* framePosition: { x: 960, y: 540 },
|
|
30
|
+
* radius: 200,
|
|
31
|
+
* objectFit: "cover",
|
|
32
|
+
* transitionDuration: 1.5
|
|
33
|
+
* }
|
|
34
|
+
* }]
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { all, waitFor } from "@twick/core";
|
|
39
|
+
import { getTimingFunction } from "../helpers/timing.utils";
|
|
40
|
+
import { fitElement } from "../helpers/element.utils";
|
|
41
|
+
import {
|
|
42
|
+
DEFAULT_POSITION,
|
|
43
|
+
DEFAULT_TIMING_FUNCTION,
|
|
44
|
+
} from "../helpers/constants";
|
|
45
|
+
import { FrameEffectParams } from "../helpers/types";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* CircleFrameEffect animates a frame transitioning into a circular shape,
|
|
49
|
+
* resizing, repositioning, and optionally fitting its content. Creates a
|
|
50
|
+
* circular mask around content perfect for profile pictures and artistic presentations.
|
|
51
|
+
*
|
|
52
|
+
* Behavior:
|
|
53
|
+
* - Waits for the specified start time
|
|
54
|
+
* - Resizes and repositions the frame container smoothly
|
|
55
|
+
* - Animates the corner radius to create a perfect circle
|
|
56
|
+
* - Repositions the content element if needed
|
|
57
|
+
* - Optionally fits the element inside the container based on object-fit
|
|
58
|
+
*
|
|
59
|
+
* @param containerRef - Reference to the frame container element
|
|
60
|
+
* @param elementRef - Reference to the content element inside the frame
|
|
61
|
+
* @param initFrameState - Initial size and position state of the element
|
|
62
|
+
* @param frameEffect - Frame transformation configuration and timing
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```js
|
|
66
|
+
* // Basic circular frame
|
|
67
|
+
* frameEffects: [{
|
|
68
|
+
* name: "circle",
|
|
69
|
+
* s: 0,
|
|
70
|
+
* e: 10,
|
|
71
|
+
* props: {
|
|
72
|
+
* frameSize: [400, 400],
|
|
73
|
+
* frameShape: "circle",
|
|
74
|
+
* framePosition: { x: 960, y: 540 },
|
|
75
|
+
* radius: 200,
|
|
76
|
+
* objectFit: "cover",
|
|
77
|
+
* transitionDuration: 1.5
|
|
78
|
+
* }
|
|
79
|
+
* }]
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export const CircleFrameEffect = {
|
|
83
|
+
name: "circle",
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generator function controlling the circle frame animation.
|
|
87
|
+
* Handles frame resizing, positioning, corner radius animation, and content fitting.
|
|
88
|
+
*
|
|
89
|
+
* @param params - Frame effect parameters including element references and configuration
|
|
90
|
+
* @returns Generator that controls the circle frame animation timeline
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```js
|
|
94
|
+
* yield* CircleFrameEffect.run({
|
|
95
|
+
* containerRef: frameContainer,
|
|
96
|
+
* elementRef: contentElement,
|
|
97
|
+
* initFrameState: initialState,
|
|
98
|
+
* frameEffect: circleConfig
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
*run({
|
|
103
|
+
containerRef,
|
|
104
|
+
elementRef,
|
|
105
|
+
initFrameState,
|
|
106
|
+
frameEffect,
|
|
107
|
+
}: FrameEffectParams) {
|
|
108
|
+
// Wait for the specified start time before starting animation
|
|
109
|
+
yield* waitFor(frameEffect.s);
|
|
110
|
+
|
|
111
|
+
const props = frameEffect.props;
|
|
112
|
+
const sequence = [];
|
|
113
|
+
|
|
114
|
+
// Animate resizing the container to target size
|
|
115
|
+
sequence.push(
|
|
116
|
+
containerRef().size(
|
|
117
|
+
{ x: props.frameSize[0], y: props.frameSize[1] },
|
|
118
|
+
props.transitionDuration,
|
|
119
|
+
getTimingFunction(props.transitionEasing ?? DEFAULT_TIMING_FUNCTION)
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Animate repositioning the container
|
|
124
|
+
sequence.push(
|
|
125
|
+
containerRef().position(
|
|
126
|
+
props.framePosition ?? { x: 0, y: 0 },
|
|
127
|
+
props.transitionDuration,
|
|
128
|
+
getTimingFunction(props.transitionEasing ?? DEFAULT_TIMING_FUNCTION)
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// Animate repositioning the element inside the container
|
|
133
|
+
sequence.push(
|
|
134
|
+
elementRef().position(
|
|
135
|
+
props.elementPosition ?? DEFAULT_POSITION,
|
|
136
|
+
props.transitionDuration,
|
|
137
|
+
getTimingFunction(props.transitionEasing ?? DEFAULT_TIMING_FUNCTION)
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Animate rounding corners to create a circle (radius = half the width)
|
|
142
|
+
sequence.push(
|
|
143
|
+
containerRef().radius(
|
|
144
|
+
props.frameSize[0] / 2,
|
|
145
|
+
props.transitionDuration,
|
|
146
|
+
getTimingFunction(props.transitionEasing ?? DEFAULT_TIMING_FUNCTION)
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// Optionally fit the element inside the resized container
|
|
151
|
+
if (props.objectFit) {
|
|
152
|
+
sequence.push(
|
|
153
|
+
fitElement({
|
|
154
|
+
elementRef,
|
|
155
|
+
containerSize: {
|
|
156
|
+
x: props.frameSize[0],
|
|
157
|
+
y: props.frameSize[1],
|
|
158
|
+
},
|
|
159
|
+
elementSize: initFrameState.element.size,
|
|
160
|
+
objectFit: props.objectFit,
|
|
161
|
+
})
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Run all animations in parallel
|
|
166
|
+
yield* all(...sequence);
|
|
167
|
+
},
|
|
168
|
+
};
|