@twick/visualizer 0.0.1
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 +21 -0
- package/README.md +13 -0
- package/package.json +31 -0
- package/src/components/animation.tsx +7 -0
- package/src/components/element.tsx +344 -0
- package/src/components/frame-effects.tsx +190 -0
- package/src/components/timeline.tsx +225 -0
- package/src/global.css +12 -0
- package/src/helpers/caption.utils.ts +40 -0
- package/src/helpers/constants.ts +159 -0
- package/src/helpers/element.utils.ts +85 -0
- package/src/helpers/filters.ts +128 -0
- package/src/helpers/log.utils.ts +32 -0
- package/src/helpers/timing.utils.ts +129 -0
- package/src/helpers/types.ts +146 -0
- package/src/index.ts +4 -0
- package/src/live.tsx +16 -0
- package/src/project.ts +6 -0
- package/src/sample.ts +450 -0
- package/src/visualizer.tsx +78 -0
- package/tsconfig.json +10 -0
- package/typedoc.json +15 -0
- package/vite.config.ts +15 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline component that handles the creation and management of different types of timelines
|
|
3
|
+
* including video, audio, captions, scenes, and elements.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Layout, Rect, View2D, Audio } from "@revideo/2d";
|
|
7
|
+
import { VisualizerTimeline } from "../helpers/types";
|
|
8
|
+
import { all, Color, createRef, waitFor } from "@revideo/core";
|
|
9
|
+
import { addAudioElement, addCaptionElement, addCircleElement, addIconElement, addMediaElement, addRectElement, addTextElement, makeSceneElements } from "./element";
|
|
10
|
+
import { CAPTION_STYLE, DEFAULT_CAPTION_COLORS, DEFAULT_CAPTION_FONT, ELEMENT_TYPES } from "../helpers/constants";
|
|
11
|
+
import { logger } from "../helpers/log.utils";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates a video timeline with specified configuration
|
|
15
|
+
* @param {Object} params - Parameters for video timeline creation
|
|
16
|
+
* @param {View2D} params.view - The 2D view to render the video in
|
|
17
|
+
* @param {VideoTimeline} params.timeline - Video timeline configuration
|
|
18
|
+
* @returns {Generator} Generator function for video timeline animation
|
|
19
|
+
*/
|
|
20
|
+
export function* makeVideoTimeline({
|
|
21
|
+
view,
|
|
22
|
+
timeline,
|
|
23
|
+
}: {
|
|
24
|
+
view: View2D;
|
|
25
|
+
timeline: VisualizerTimeline;
|
|
26
|
+
}) {
|
|
27
|
+
const frameRef = createRef<any>();
|
|
28
|
+
let prevTime = 0;
|
|
29
|
+
view.add(<Layout size={"100%"} ref={frameRef} layout />);
|
|
30
|
+
for (const element of timeline.elements || []) {
|
|
31
|
+
yield* waitFor(element?.s - prevTime);
|
|
32
|
+
yield* addMediaElement({
|
|
33
|
+
containerRef: frameRef,
|
|
34
|
+
element,
|
|
35
|
+
mediaType: ELEMENT_TYPES.VIDEO,
|
|
36
|
+
waitOnStart: false,
|
|
37
|
+
});
|
|
38
|
+
prevTime = element.e;
|
|
39
|
+
}
|
|
40
|
+
yield frameRef().remove();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates an audio timeline with specified configuration
|
|
45
|
+
* @param {Object} params - Parameters for audio timeline creation
|
|
46
|
+
* @param {View2D} params.view - The 2D view to render the audio in
|
|
47
|
+
* @param {AudioTimeline} params.timeline - Audio timeline configuration
|
|
48
|
+
* @returns {Generator} Generator function for audio timeline animation
|
|
49
|
+
*/
|
|
50
|
+
export function* makeAudioTimeline({
|
|
51
|
+
view,
|
|
52
|
+
timeline,
|
|
53
|
+
}: {
|
|
54
|
+
view: View2D;
|
|
55
|
+
timeline: VisualizerTimeline;
|
|
56
|
+
}) {
|
|
57
|
+
let prevTime = 0;
|
|
58
|
+
for (const audioElement of timeline.elements) {
|
|
59
|
+
const audioRef = createRef<any>();
|
|
60
|
+
yield* waitFor(audioElement?.s - prevTime);
|
|
61
|
+
prevTime = audioElement?.e;
|
|
62
|
+
logger(`Adding audio element ${audioElement.id}`);
|
|
63
|
+
view.add(
|
|
64
|
+
<Audio ref={audioRef} key={audioElement.id} {...audioElement.props} />
|
|
65
|
+
);
|
|
66
|
+
yield* waitFor(Math.max(0, audioElement.e - audioElement.s));
|
|
67
|
+
yield audioRef().playing(false);
|
|
68
|
+
yield audioRef().remove();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a caption timeline with specified configuration
|
|
74
|
+
* @param {Object} params - Parameters for caption timeline creation
|
|
75
|
+
* @param {View2D} params.view - The 2D view to render the caption in
|
|
76
|
+
* @param {CaptionTimeline} params.timeline - Caption timeline configuration
|
|
77
|
+
* @returns {Generator} Generator function for caption timeline animation
|
|
78
|
+
*/
|
|
79
|
+
export function* makeCaptionTimeline({ view, timeline }: { view: View2D; timeline: VisualizerTimeline }) {
|
|
80
|
+
let prevTime = 0;
|
|
81
|
+
const captionTimelineRef = createRef<any>();
|
|
82
|
+
view.add(<Layout size={"100%"} ref={captionTimelineRef} />);
|
|
83
|
+
|
|
84
|
+
const tProps = timeline?.props;
|
|
85
|
+
|
|
86
|
+
const timelineDefaultProps = (CAPTION_STYLE[tProps?.capStyle ?? ""] || {}).word || {};
|
|
87
|
+
|
|
88
|
+
for (const element of timeline.elements) {
|
|
89
|
+
const eProps = element.props;
|
|
90
|
+
const rectStyle = (CAPTION_STYLE[eProps?.capStyle ?? tProps?.capStyle ?? ""] || {}).rect || {};
|
|
91
|
+
// Cast alignItems/justifyContent as any to satisfy RectProps
|
|
92
|
+
const mappedRectStyle = {
|
|
93
|
+
...rectStyle,
|
|
94
|
+
justifyContent: rectStyle.justifyContent as any,
|
|
95
|
+
alignItems: rectStyle.alignItems as any,
|
|
96
|
+
};
|
|
97
|
+
const phraseProps = {
|
|
98
|
+
...timelineDefaultProps,
|
|
99
|
+
colors: {
|
|
100
|
+
text: eProps?.colors?.text ?? tProps?.colors?.text ?? DEFAULT_CAPTION_COLORS.text,
|
|
101
|
+
background: eProps?.colors?.background ?? tProps?.colors?.background ?? DEFAULT_CAPTION_COLORS.background,
|
|
102
|
+
},
|
|
103
|
+
font: {
|
|
104
|
+
family: eProps?.font?.family ?? tProps?.font?.family ?? DEFAULT_CAPTION_FONT.family,
|
|
105
|
+
size: eProps?.font?.size ?? tProps?.font?.size ?? DEFAULT_CAPTION_FONT.size,
|
|
106
|
+
weight: eProps?.font?.weight ?? tProps?.font?.weight ?? DEFAULT_CAPTION_FONT.weight
|
|
107
|
+
},
|
|
108
|
+
fill: eProps?.colors?.text ?? tProps?.colors?.text ?? DEFAULT_CAPTION_COLORS.text,
|
|
109
|
+
bgColor: eProps?.colors?.background ?? tProps?.colors?.background ??DEFAULT_CAPTION_COLORS.background,
|
|
110
|
+
bgOpacity: eProps?.bgOpacity ?? tProps?.bgOpacity ?? 1,
|
|
111
|
+
...(tProps?.captionProps || {}),
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
yield* waitFor(element?.s - prevTime);
|
|
115
|
+
const phraseRef = createRef<any>();
|
|
116
|
+
captionTimelineRef().add(
|
|
117
|
+
<Rect
|
|
118
|
+
ref={phraseRef}
|
|
119
|
+
key={element.id}
|
|
120
|
+
{...mappedRectStyle}
|
|
121
|
+
x={eProps?.x ?? tProps?.x}
|
|
122
|
+
y={eProps?.y ?? tProps?.y}
|
|
123
|
+
layout
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
if (tProps?.capStyle === "word_by_word_with_bg") {
|
|
127
|
+
phraseRef().fill(
|
|
128
|
+
new Color(phraseProps.bgColor).alpha(phraseProps.bgOpacity)
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
yield* addCaptionElement({
|
|
132
|
+
caption: {...element, t: element.t ?? ""},
|
|
133
|
+
captionProps: phraseProps,
|
|
134
|
+
containerRef: phraseRef,
|
|
135
|
+
capStyle: eProps?.capStyle ?? tProps?.capStyle,
|
|
136
|
+
});
|
|
137
|
+
prevTime = element.e;
|
|
138
|
+
yield phraseRef().remove();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Creates a scene timeline with specified configuration
|
|
144
|
+
* @param {Object} params - Parameters for scene timeline creation
|
|
145
|
+
* @param {View2D} params.view - The 2D view to render the scene in
|
|
146
|
+
* @param {SceneTimeline} params.timeline - Scene timeline configuration
|
|
147
|
+
* @returns {Generator} Generator function for scene timeline animation
|
|
148
|
+
*/
|
|
149
|
+
export function* makeSceneTimeline({ view, timeline }: { view: View2D; timeline: VisualizerTimeline }) {
|
|
150
|
+
const frameRef = createRef<any>();
|
|
151
|
+
let prevTime = 0;
|
|
152
|
+
view.add(<Layout size={"100%"} ref={frameRef} layout />);
|
|
153
|
+
for (const sceneElement of timeline.elements || []) {
|
|
154
|
+
yield* waitFor(sceneElement?.s - prevTime);
|
|
155
|
+
yield* makeSceneElements({
|
|
156
|
+
containerRef: frameRef,
|
|
157
|
+
element: sceneElement,
|
|
158
|
+
});
|
|
159
|
+
prevTime = sceneElement.e;
|
|
160
|
+
}
|
|
161
|
+
yield frameRef().remove();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Creates an element timeline with specified configuration
|
|
166
|
+
* @param {Object} params - Parameters for element timeline creation
|
|
167
|
+
* @param {View2D} params.view - The 2D view to render the element in
|
|
168
|
+
* @param {ElementTimeline} params.timeline - Element timeline configuration
|
|
169
|
+
* @returns {Generator} Generator function for element timeline animation
|
|
170
|
+
*/
|
|
171
|
+
export function* makeElementTimeline({ view, timeline }: { view: View2D; timeline: VisualizerTimeline }) {
|
|
172
|
+
const elementTimelineRef = createRef<any>();
|
|
173
|
+
view.add(<Layout size={"100%"} ref={elementTimelineRef} />);
|
|
174
|
+
|
|
175
|
+
const sequence = [];
|
|
176
|
+
for (const element of timeline.elements) {
|
|
177
|
+
switch (element.type) {
|
|
178
|
+
case ELEMENT_TYPES.RECT:
|
|
179
|
+
sequence.push(
|
|
180
|
+
addRectElement({ containerRef: elementTimelineRef, element })
|
|
181
|
+
);
|
|
182
|
+
break;
|
|
183
|
+
case ELEMENT_TYPES.TEXT:
|
|
184
|
+
sequence.push(
|
|
185
|
+
addTextElement({ containerRef: elementTimelineRef, element })
|
|
186
|
+
);
|
|
187
|
+
break;
|
|
188
|
+
case ELEMENT_TYPES.IMAGE:
|
|
189
|
+
sequence.push(
|
|
190
|
+
addMediaElement({
|
|
191
|
+
containerRef: elementTimelineRef,
|
|
192
|
+
element,
|
|
193
|
+
mediaType: ELEMENT_TYPES.IMAGE,
|
|
194
|
+
})
|
|
195
|
+
);
|
|
196
|
+
break;
|
|
197
|
+
case ELEMENT_TYPES.VIDEO:
|
|
198
|
+
sequence.push(
|
|
199
|
+
addMediaElement({
|
|
200
|
+
containerRef: elementTimelineRef,
|
|
201
|
+
element,
|
|
202
|
+
mediaType: ELEMENT_TYPES.VIDEO,
|
|
203
|
+
})
|
|
204
|
+
);
|
|
205
|
+
break;
|
|
206
|
+
case ELEMENT_TYPES.AUDIO:
|
|
207
|
+
sequence.push(
|
|
208
|
+
addAudioElement({ containerRef: elementTimelineRef, element })
|
|
209
|
+
);
|
|
210
|
+
break;
|
|
211
|
+
case ELEMENT_TYPES.CIRCLE:
|
|
212
|
+
sequence.push(
|
|
213
|
+
addCircleElement({ containerRef: elementTimelineRef, element })
|
|
214
|
+
);
|
|
215
|
+
break;
|
|
216
|
+
case ELEMENT_TYPES.ICON:
|
|
217
|
+
sequence.push(
|
|
218
|
+
addIconElement({ containerRef: elementTimelineRef, element })
|
|
219
|
+
);
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
yield* all(...sequence);
|
|
224
|
+
yield elementTimelineRef().remove();
|
|
225
|
+
}
|
package/src/global.css
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
@import url('https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&display=swap');
|
|
2
|
+
@import url('https://fonts.googleapis.com/css2?family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap');
|
|
3
|
+
@import url('https://fonts.googleapis.com/css2?family=Luckiest+Guy&family=Mulish:ital,wght@0,200..1000;1,200..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap');
|
|
4
|
+
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');
|
|
5
|
+
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Birthstone&family=Corinthia:wght@400;700&family=Imperial+Script&family=Kumar+One+Outline&family=Londrina+Outline&family=Marck+Script&family=Montserrat:ital,wght@0,100..900;1,100..900&family=Pattaya&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
|
6
|
+
@import url('https://fonts.cdnfonts.com/css/peralta');
|
|
7
|
+
@import url('https://fonts.cdnfonts.com/css/impact');
|
|
8
|
+
@import url('https://fonts.cdnfonts.com/css/lumanosimo');
|
|
9
|
+
@import url('https://fonts.cdnfonts.com/css/kapakana');
|
|
10
|
+
@import url('https://fonts.cdnfonts.com/css/handyrush');
|
|
11
|
+
@import url('https://fonts.cdnfonts.com/css/dasher');
|
|
12
|
+
@import url('https://fonts.cdnfonts.com/css/brittany-signature');
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caption utilities for the visualizer package.
|
|
3
|
+
* Provides functions for handling caption text formatting and timing.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Splits a phrase into words with timing information
|
|
8
|
+
* @param {string} phrase - The phrase to split
|
|
9
|
+
* @param {number} startTime - Start time of the phrase
|
|
10
|
+
* @param {number} duration - Duration of the phrase
|
|
11
|
+
* @returns {Array} Array of words with timing information
|
|
12
|
+
*/
|
|
13
|
+
export function splitPhraseTiming({ t, s, e }: { t: string, s: number, e: number }) {
|
|
14
|
+
const words = t.split(" ");
|
|
15
|
+
const totalDuration = e - s;
|
|
16
|
+
const totalLength = words.join("").length; // Total character length without spaces
|
|
17
|
+
|
|
18
|
+
let currentTime = s;
|
|
19
|
+
return words.map((word) => {
|
|
20
|
+
const wordDuration = (word.length / totalLength) * totalDuration;
|
|
21
|
+
const wordStart = currentTime;
|
|
22
|
+
const wordEnd = wordStart + wordDuration;
|
|
23
|
+
currentTime = wordEnd;
|
|
24
|
+
return {
|
|
25
|
+
t: word,
|
|
26
|
+
s: parseFloat(wordStart.toFixed(2)),
|
|
27
|
+
e: parseFloat(wordEnd.toFixed(2)),
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Formats caption text with specified style
|
|
34
|
+
* @param {string} text - The text to format
|
|
35
|
+
* @param {Object} style - The style to apply
|
|
36
|
+
* @returns {string} Formatted text
|
|
37
|
+
*/
|
|
38
|
+
export function formatCaptionText(text: string, style: any) {
|
|
39
|
+
// ... existing code ...
|
|
40
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { CaptionStyle } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Constants used throughout the visualizer package.
|
|
5
|
+
* Contains default values, configuration options, and type definitions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Default background color for scenes
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_BACKGROUND_COLOR = "#000000";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Default position for elements
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_POSITION = {
|
|
17
|
+
x: 0,
|
|
18
|
+
y: 0
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Default timing function for animations
|
|
23
|
+
*/
|
|
24
|
+
export const DEFAULT_TIMING_FUNCTION = "easeInOut";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Available frame shapes for elements
|
|
28
|
+
*/
|
|
29
|
+
export const FRAME_SHAPE = {
|
|
30
|
+
RECTANGLE: "rectangle",
|
|
31
|
+
CIRCLE: "circle",
|
|
32
|
+
LINE: "line"
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Timeline types for different media elements
|
|
37
|
+
*/
|
|
38
|
+
export const TIMELINE_TYPES = {
|
|
39
|
+
VIDEO: "video",
|
|
40
|
+
AUDIO: "audio",
|
|
41
|
+
CAPTION: "caption",
|
|
42
|
+
SCENE: "scene",
|
|
43
|
+
ELEMENT: "element"
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
export const CAPTION_STYLE: Record<string, CaptionStyle> = {
|
|
47
|
+
highlight_bg: {
|
|
48
|
+
rect: {
|
|
49
|
+
alignItems: "center",
|
|
50
|
+
gap: 2,
|
|
51
|
+
},
|
|
52
|
+
word: {
|
|
53
|
+
lineWidth: 0.35,
|
|
54
|
+
stroke: "#000000",
|
|
55
|
+
fontWeight: 700,
|
|
56
|
+
shadowOffset: [-3, 3],
|
|
57
|
+
shadowColor: "#000000",
|
|
58
|
+
fill: "#ffffff",
|
|
59
|
+
fontFamily: "Bangers",
|
|
60
|
+
bgColor: "#000000",
|
|
61
|
+
bgOffsetWidth: 30,
|
|
62
|
+
bgOffsetHeight: 8,
|
|
63
|
+
fontSize: 54,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
word_by_word: {
|
|
67
|
+
rect: {
|
|
68
|
+
alignItems: "center",
|
|
69
|
+
justifyContent: "center",
|
|
70
|
+
gap: 12,
|
|
71
|
+
},
|
|
72
|
+
word: {
|
|
73
|
+
lineWidth: 0.35,
|
|
74
|
+
stroke: "#000000",
|
|
75
|
+
fontWeight: 700,
|
|
76
|
+
strokeFirst: true,
|
|
77
|
+
shadowOffset: [-2, 2],
|
|
78
|
+
shadowColor: "#000000",
|
|
79
|
+
shadowBlur: 5,
|
|
80
|
+
fontFamily: "Bangers",
|
|
81
|
+
fill: "#FFFFFF",
|
|
82
|
+
bgOffsetWidth: 20,
|
|
83
|
+
bgOffsetHeight: 10,
|
|
84
|
+
fontSize: 54,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
word_by_word_with_bg: {
|
|
88
|
+
rect: {
|
|
89
|
+
alignItems: "center",
|
|
90
|
+
gap: 12,
|
|
91
|
+
padding: [10, 20],
|
|
92
|
+
radius: 10,
|
|
93
|
+
},
|
|
94
|
+
word: {
|
|
95
|
+
lineWidth: 0.35,
|
|
96
|
+
stroke: "#000000",
|
|
97
|
+
fontWeight: 700,
|
|
98
|
+
strokeFirst: true,
|
|
99
|
+
shadowOffset: [-2, 2],
|
|
100
|
+
shadowColor: "#000000",
|
|
101
|
+
shadowBlur: 5,
|
|
102
|
+
fontFamily: "Bangers",
|
|
103
|
+
fill: "#FFFFFF",
|
|
104
|
+
bgOffsetWidth: 20,
|
|
105
|
+
bgOffsetHeight: 10,
|
|
106
|
+
fontSize: 54,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const DEFAULT_CAPTION_COLORS = {
|
|
112
|
+
text: "#000000",
|
|
113
|
+
background: "#FFFFFF",
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const DEFAULT_CAPTION_FONT = {
|
|
117
|
+
family: "Poppins",
|
|
118
|
+
size: 48,
|
|
119
|
+
weight: 400,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const TRANSPARENT_COLOR = "#FFFFFF00";
|
|
123
|
+
|
|
124
|
+
export const ELEMENT_TYPES = {
|
|
125
|
+
VIDEO: "video",
|
|
126
|
+
IMAGE: "image",
|
|
127
|
+
AUDIO: "audio",
|
|
128
|
+
TEXT: "text",
|
|
129
|
+
CAPTION: "caption",
|
|
130
|
+
RECT: "rect",
|
|
131
|
+
CIRCLE: "circle",
|
|
132
|
+
ICON: "icon",
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const OBJECT_FIT = {
|
|
136
|
+
CONTAIN: "contain",
|
|
137
|
+
COVER: "cover",
|
|
138
|
+
FILL: "fill",
|
|
139
|
+
NONE: "none",
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const COLOR_FILTERS = {
|
|
143
|
+
SATURATED: "saturated",
|
|
144
|
+
BRIGHT: "bright",
|
|
145
|
+
VIBRANT: "vibrant",
|
|
146
|
+
RETRO: "retro",
|
|
147
|
+
BLACK_WHITE: "blackWhite",
|
|
148
|
+
SEPIA: "sepia",
|
|
149
|
+
COOL: "cool",
|
|
150
|
+
WARM: "warm",
|
|
151
|
+
CINEMATIC: "cinematic",
|
|
152
|
+
SOFT_GLOW: "softGlow",
|
|
153
|
+
MOODY: "moody",
|
|
154
|
+
DREAMY: "dreamy",
|
|
155
|
+
INVERTED: "inverted",
|
|
156
|
+
VINTAGE: "vintage",
|
|
157
|
+
DRAMATIC: "dramatic",
|
|
158
|
+
FADED: "faded",
|
|
159
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Reference } from "@revideo/core";
|
|
2
|
+
import { ObjectFit, SizeVector } from "./types";
|
|
3
|
+
import { OBJECT_FIT } from "./constants";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Element utilities for the visualizer package.
|
|
7
|
+
* Provides functions for handling element positioning, sizing, and transformations.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Fits an element within specified bounds while maintaining aspect ratio
|
|
12
|
+
* @param {Object} params - Parameters for fitting the element
|
|
13
|
+
* @param {Object} params.containerSize - The container size
|
|
14
|
+
* @param {Object} params.elementSize - The element size
|
|
15
|
+
* @param {Object} params.elementRef - The element reference
|
|
16
|
+
* @param {string} [params.objectFit] - The fit mode (contain, cover, fill)
|
|
17
|
+
* @returns {Object} Updated element dimensions
|
|
18
|
+
*/
|
|
19
|
+
export function* fitElement({
|
|
20
|
+
containerSize,
|
|
21
|
+
elementSize,
|
|
22
|
+
elementRef,
|
|
23
|
+
objectFit
|
|
24
|
+
}: {
|
|
25
|
+
containerSize: SizeVector;
|
|
26
|
+
elementSize: SizeVector;
|
|
27
|
+
elementRef: Reference<any>;
|
|
28
|
+
objectFit?: ObjectFit;
|
|
29
|
+
}) {
|
|
30
|
+
const containerAspectRatio = containerSize.x / containerSize.y;
|
|
31
|
+
const elementAspectRatio = elementSize.x / elementSize.y;
|
|
32
|
+
switch (objectFit) {
|
|
33
|
+
case OBJECT_FIT.CONTAIN:
|
|
34
|
+
if (elementAspectRatio > containerAspectRatio) {
|
|
35
|
+
yield elementRef().size({
|
|
36
|
+
x: containerSize.x,
|
|
37
|
+
y: containerSize.x / elementAspectRatio,
|
|
38
|
+
});
|
|
39
|
+
yield elementRef().scale(
|
|
40
|
+
containerSize.x / elementSize.x,
|
|
41
|
+
(containerSize.x * elementAspectRatio) / elementSize.y
|
|
42
|
+
);
|
|
43
|
+
} else {
|
|
44
|
+
yield elementRef().size({
|
|
45
|
+
x: containerSize.y * elementAspectRatio,
|
|
46
|
+
y: containerSize.y,
|
|
47
|
+
});
|
|
48
|
+
yield elementRef().scale(
|
|
49
|
+
(containerSize.y * elementAspectRatio) / elementSize.x,
|
|
50
|
+
containerSize.y / elementSize.y
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
case OBJECT_FIT.COVER:
|
|
55
|
+
if (elementAspectRatio > containerAspectRatio) {
|
|
56
|
+
yield elementRef().size({
|
|
57
|
+
x: containerSize.y * elementAspectRatio,
|
|
58
|
+
y: containerSize.y,
|
|
59
|
+
});
|
|
60
|
+
yield elementRef().scale(
|
|
61
|
+
(containerSize.y * elementAspectRatio) / elementSize.x,
|
|
62
|
+
containerSize.y / elementSize.y
|
|
63
|
+
);
|
|
64
|
+
} else {
|
|
65
|
+
yield elementRef().size({
|
|
66
|
+
x: containerSize.x,
|
|
67
|
+
y: containerSize.x / elementAspectRatio,
|
|
68
|
+
});
|
|
69
|
+
yield elementRef().scale(
|
|
70
|
+
containerSize.x / elementSize.x,
|
|
71
|
+
(containerSize.x * elementAspectRatio) / elementSize.y
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
case OBJECT_FIT.FILL:
|
|
76
|
+
yield elementRef().size(containerSize);
|
|
77
|
+
yield elementRef().scale(
|
|
78
|
+
containerSize.x / elementSize.x,
|
|
79
|
+
containerSize.x / elementSize.y
|
|
80
|
+
);
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Reference } from "@revideo/core";
|
|
2
|
+
import { COLOR_FILTERS } from "./constants";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Filter utilities for the visualizer package.
|
|
6
|
+
* Provides functions for applying various visual filters to elements.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Applies a blur filter to an element
|
|
11
|
+
* @param {Object} params - Parameters for the blur filter
|
|
12
|
+
* @param {Reference<any>} params.element - The element to apply the filter to
|
|
13
|
+
* @param {number} params.amount - The blur amount
|
|
14
|
+
* @returns {void}
|
|
15
|
+
*/
|
|
16
|
+
export function applyBlurFilter({ element, amount }: { element: Reference<any>; amount: number }) {
|
|
17
|
+
// ... existing code ...
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Applies a brightness filter to an element
|
|
22
|
+
* @param {Object} params - Parameters for the brightness filter
|
|
23
|
+
* @param {Reference<any>} params.element - The element to apply the filter to
|
|
24
|
+
* @param {number} params.amount - The brightness amount
|
|
25
|
+
* @returns {void}
|
|
26
|
+
*/
|
|
27
|
+
export function applyBrightnessFilter({ element, amount }: { element: Reference<any>; amount: number }) {
|
|
28
|
+
// ... existing code ...
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Applies a contrast filter to an element
|
|
33
|
+
* @param {Object} params - Parameters for the contrast filter
|
|
34
|
+
* @param {Reference<any>} params.element - The element to apply the filter to
|
|
35
|
+
* @param {number} params.amount - The contrast amount
|
|
36
|
+
* @returns {void}
|
|
37
|
+
*/
|
|
38
|
+
export function applyContrastFilter({ element, amount }: { element: Reference<any>; amount: number }) {
|
|
39
|
+
// ... existing code ...
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const applyColorFilter = (ref: Reference<any>, filterType: string) => {
|
|
43
|
+
switch (filterType) {
|
|
44
|
+
case COLOR_FILTERS.SATURATED:
|
|
45
|
+
ref().filters.saturate(1.4);
|
|
46
|
+
ref().filters.contrast(1.1);
|
|
47
|
+
break;
|
|
48
|
+
case COLOR_FILTERS.BRIGHT:
|
|
49
|
+
ref().filters.brightness(1.3);
|
|
50
|
+
ref().filters.contrast(1.05);
|
|
51
|
+
break;
|
|
52
|
+
case COLOR_FILTERS.VIBRANT:
|
|
53
|
+
ref().filters.saturate(1.6);
|
|
54
|
+
ref().filters.brightness(1.15);
|
|
55
|
+
ref().filters.contrast(1.1);
|
|
56
|
+
break;
|
|
57
|
+
case COLOR_FILTERS.RETRO:
|
|
58
|
+
ref().filters.sepia(0.8);
|
|
59
|
+
ref().filters.contrast(1.3);
|
|
60
|
+
ref().filters.brightness(0.85);
|
|
61
|
+
ref().filters.saturate(0.8);
|
|
62
|
+
break;
|
|
63
|
+
case COLOR_FILTERS.BLACK_WHITE:
|
|
64
|
+
ref().filters.grayscale(1);
|
|
65
|
+
ref().filters.contrast(1.25);
|
|
66
|
+
ref().filters.brightness(1.05);
|
|
67
|
+
break;
|
|
68
|
+
case COLOR_FILTERS.COOL:
|
|
69
|
+
ref().filters.hue(15);
|
|
70
|
+
ref().filters.brightness(1.1);
|
|
71
|
+
ref().filters.saturate(1.3);
|
|
72
|
+
ref().filters.contrast(1.05);
|
|
73
|
+
break;
|
|
74
|
+
case COLOR_FILTERS.WARM:
|
|
75
|
+
ref().filters.hue(-15);
|
|
76
|
+
ref().filters.brightness(1.15);
|
|
77
|
+
ref().filters.saturate(1.3);
|
|
78
|
+
ref().filters.contrast(1.05);
|
|
79
|
+
break;
|
|
80
|
+
case COLOR_FILTERS.CINEMATIC:
|
|
81
|
+
ref().filters.contrast(1.4);
|
|
82
|
+
ref().filters.brightness(0.95);
|
|
83
|
+
ref().filters.saturate(0.85);
|
|
84
|
+
ref().filters.sepia(0.2);
|
|
85
|
+
break;
|
|
86
|
+
case COLOR_FILTERS.SOFT_GLOW:
|
|
87
|
+
ref().filters.brightness(1.2);
|
|
88
|
+
ref().filters.contrast(0.95);
|
|
89
|
+
ref().filters.blur(1.2);
|
|
90
|
+
ref().filters.saturate(1.1);
|
|
91
|
+
break;
|
|
92
|
+
case COLOR_FILTERS.MOODY:
|
|
93
|
+
ref().filters.brightness(1.05);
|
|
94
|
+
ref().filters.contrast(1.4);
|
|
95
|
+
ref().filters.saturate(0.65);
|
|
96
|
+
ref().filters.sepia(0.2);
|
|
97
|
+
break;
|
|
98
|
+
case COLOR_FILTERS.DREAMY:
|
|
99
|
+
ref().filters.brightness(1.3);
|
|
100
|
+
ref().filters.blur(2);
|
|
101
|
+
ref().filters.saturate(1.4);
|
|
102
|
+
ref().filters.contrast(0.95);
|
|
103
|
+
break;
|
|
104
|
+
case COLOR_FILTERS.INVERTED:
|
|
105
|
+
ref().filters.invert(1);
|
|
106
|
+
ref().filters.hue(180);
|
|
107
|
+
break;
|
|
108
|
+
case COLOR_FILTERS.VINTAGE:
|
|
109
|
+
ref().filters.sepia(0.4);
|
|
110
|
+
ref().filters.saturate(1.4);
|
|
111
|
+
ref().filters.contrast(1.2);
|
|
112
|
+
ref().filters.brightness(1.1);
|
|
113
|
+
break;
|
|
114
|
+
case COLOR_FILTERS.DRAMATIC:
|
|
115
|
+
ref().filters.contrast(1.5);
|
|
116
|
+
ref().filters.brightness(0.9);
|
|
117
|
+
ref().filters.saturate(1.2);
|
|
118
|
+
break;
|
|
119
|
+
case COLOR_FILTERS.FADED:
|
|
120
|
+
ref().filters.opacity(0.9);
|
|
121
|
+
ref().filters.brightness(1.2);
|
|
122
|
+
ref().filters.saturate(0.8);
|
|
123
|
+
ref().filters.contrast(0.9);
|
|
124
|
+
break;
|
|
125
|
+
default:
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useLogger } from "@revideo/core";
|
|
2
|
+
import { format } from "date-fns";
|
|
3
|
+
|
|
4
|
+
const loggerInstance = useLogger();
|
|
5
|
+
|
|
6
|
+
const getCurrentTime = (dateFormat = "mm:ss:SSS") => {
|
|
7
|
+
const now = new Date();
|
|
8
|
+
return format(now, dateFormat);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Logging utilities for the visualizer package.
|
|
13
|
+
* Provides consistent logging functionality across the application.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Logs a message to the console with a visualizer prefix
|
|
18
|
+
* @param {string} message - The message to log
|
|
19
|
+
* @param {any} [data] - Optional data to log
|
|
20
|
+
*/
|
|
21
|
+
export function logger(message: string, data?: any) {
|
|
22
|
+
console.log(`[Visualizer] ${message}`, data ? data : "");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Logs an error to the console with a visualizer prefix
|
|
27
|
+
* @param {string} message - The error message to log
|
|
28
|
+
* @param {Error} [error] - Optional error object
|
|
29
|
+
*/
|
|
30
|
+
export function errorLogger(message: string, error?: Error) {
|
|
31
|
+
console.error(`[Visualizer Error] ${message}`, error ? error : "");
|
|
32
|
+
}
|