@shotstack/shotstack-canvas 2.1.5 → 2.1.7
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/entry.node.cjs +84 -101
- package/dist/entry.node.d.cts +13 -28
- package/dist/entry.node.d.ts +13 -28
- package/dist/entry.node.js +85 -103
- package/dist/entry.web.d.ts +13 -27
- package/dist/entry.web.js +78 -90
- package/package.json +2 -2
package/dist/entry.node.d.ts
CHANGED
|
@@ -271,11 +271,6 @@ declare const richCaptionAssetSchema: z.ZodObject<{
|
|
|
271
271
|
color: z.ZodDefault<z.ZodString>;
|
|
272
272
|
opacity: z.ZodDefault<z.ZodNumber>;
|
|
273
273
|
background: z.ZodOptional<z.ZodString>;
|
|
274
|
-
textDecoration: z.ZodDefault<z.ZodEnum<{
|
|
275
|
-
none: "none";
|
|
276
|
-
underline: "underline";
|
|
277
|
-
"line-through": "line-through";
|
|
278
|
-
}>>;
|
|
279
274
|
}, z.core.$strip>>;
|
|
280
275
|
style: z.ZodOptional<z.ZodObject<{
|
|
281
276
|
wordSpacing: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>, z.ZodString]>>>;
|
|
@@ -356,39 +351,38 @@ declare const richCaptionAssetSchema: z.ZodObject<{
|
|
|
356
351
|
"line-through": "line-through";
|
|
357
352
|
}>>;
|
|
358
353
|
}, z.core.$strip>>;
|
|
359
|
-
stroke: z.ZodOptional<z.ZodObject<{
|
|
354
|
+
stroke: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
360
355
|
width: z.ZodOptional<z.ZodNumber>;
|
|
361
356
|
color: z.ZodOptional<z.ZodString>;
|
|
362
357
|
opacity: z.ZodOptional<z.ZodNumber>;
|
|
363
|
-
}, z.core.$strip>>;
|
|
364
|
-
shadow: z.ZodOptional<z.ZodObject<{
|
|
358
|
+
}, z.core.$strip>, z.ZodLiteral<"none">]>>;
|
|
359
|
+
shadow: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
365
360
|
offsetX: z.ZodOptional<z.ZodNumber>;
|
|
366
361
|
offsetY: z.ZodOptional<z.ZodNumber>;
|
|
367
362
|
blur: z.ZodOptional<z.ZodNumber>;
|
|
368
363
|
color: z.ZodOptional<z.ZodString>;
|
|
369
364
|
opacity: z.ZodOptional<z.ZodNumber>;
|
|
370
|
-
}, z.core.$strip>>;
|
|
365
|
+
}, z.core.$strip>, z.ZodLiteral<"none">]>>;
|
|
371
366
|
scale: z.ZodDefault<z.ZodNumber>;
|
|
372
367
|
}, z.core.$strict>>;
|
|
373
368
|
wordAnimation: z.ZodOptional<z.ZodObject<{
|
|
374
369
|
style: z.ZodDefault<z.ZodEnum<{
|
|
375
370
|
typewriter: "typewriter";
|
|
376
371
|
none: "none";
|
|
372
|
+
pop: "pop";
|
|
377
373
|
karaoke: "karaoke";
|
|
378
374
|
highlight: "highlight";
|
|
379
|
-
pop: "pop";
|
|
380
375
|
fade: "fade";
|
|
381
376
|
slide: "slide";
|
|
382
377
|
bounce: "bounce";
|
|
383
378
|
}>>;
|
|
384
|
-
speed: z.ZodDefault<z.ZodNumber>;
|
|
385
379
|
direction: z.ZodDefault<z.ZodEnum<{
|
|
386
380
|
right: "right";
|
|
387
381
|
left: "left";
|
|
388
382
|
up: "up";
|
|
389
383
|
down: "down";
|
|
390
384
|
}>>;
|
|
391
|
-
}, z.core.$
|
|
385
|
+
}, z.core.$strip>>;
|
|
392
386
|
pauseThreshold: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
393
387
|
customFonts: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
394
388
|
src: z.ZodString;
|
|
@@ -414,11 +408,6 @@ declare const CanvasRichCaptionAssetSchema: z.ZodObject<{
|
|
|
414
408
|
color: z.ZodDefault<z.ZodString>;
|
|
415
409
|
opacity: z.ZodDefault<z.ZodNumber>;
|
|
416
410
|
background: z.ZodOptional<z.ZodString>;
|
|
417
|
-
textDecoration: z.ZodDefault<z.ZodEnum<{
|
|
418
|
-
none: "none";
|
|
419
|
-
underline: "underline";
|
|
420
|
-
"line-through": "line-through";
|
|
421
|
-
}>>;
|
|
422
411
|
}, z.core.$strip>>;
|
|
423
412
|
style: z.ZodOptional<z.ZodObject<{
|
|
424
413
|
wordSpacing: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>, z.ZodString]>>>;
|
|
@@ -499,39 +488,38 @@ declare const CanvasRichCaptionAssetSchema: z.ZodObject<{
|
|
|
499
488
|
"line-through": "line-through";
|
|
500
489
|
}>>;
|
|
501
490
|
}, z.core.$strip>>;
|
|
502
|
-
stroke: z.ZodOptional<z.ZodObject<{
|
|
491
|
+
stroke: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
503
492
|
width: z.ZodOptional<z.ZodNumber>;
|
|
504
493
|
color: z.ZodOptional<z.ZodString>;
|
|
505
494
|
opacity: z.ZodOptional<z.ZodNumber>;
|
|
506
|
-
}, z.core.$strip>>;
|
|
507
|
-
shadow: z.ZodOptional<z.ZodObject<{
|
|
495
|
+
}, z.core.$strip>, z.ZodLiteral<"none">]>>;
|
|
496
|
+
shadow: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
508
497
|
offsetX: z.ZodOptional<z.ZodNumber>;
|
|
509
498
|
offsetY: z.ZodOptional<z.ZodNumber>;
|
|
510
499
|
blur: z.ZodOptional<z.ZodNumber>;
|
|
511
500
|
color: z.ZodOptional<z.ZodString>;
|
|
512
501
|
opacity: z.ZodOptional<z.ZodNumber>;
|
|
513
|
-
}, z.core.$strip>>;
|
|
502
|
+
}, z.core.$strip>, z.ZodLiteral<"none">]>>;
|
|
514
503
|
scale: z.ZodDefault<z.ZodNumber>;
|
|
515
504
|
}, z.core.$strict>>;
|
|
516
505
|
wordAnimation: z.ZodOptional<z.ZodObject<{
|
|
517
506
|
style: z.ZodDefault<z.ZodEnum<{
|
|
518
507
|
typewriter: "typewriter";
|
|
519
508
|
none: "none";
|
|
509
|
+
pop: "pop";
|
|
520
510
|
karaoke: "karaoke";
|
|
521
511
|
highlight: "highlight";
|
|
522
|
-
pop: "pop";
|
|
523
512
|
fade: "fade";
|
|
524
513
|
slide: "slide";
|
|
525
514
|
bounce: "bounce";
|
|
526
515
|
}>>;
|
|
527
|
-
speed: z.ZodDefault<z.ZodNumber>;
|
|
528
516
|
direction: z.ZodDefault<z.ZodEnum<{
|
|
529
517
|
right: "right";
|
|
530
518
|
left: "left";
|
|
531
519
|
up: "up";
|
|
532
520
|
down: "down";
|
|
533
521
|
}>>;
|
|
534
|
-
}, z.core.$
|
|
522
|
+
}, z.core.$strip>>;
|
|
535
523
|
pauseThreshold: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
536
524
|
customFonts: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
537
525
|
src: z.ZodString;
|
|
@@ -663,7 +651,6 @@ interface CaptionLayoutConfig {
|
|
|
663
651
|
fontFamily: string;
|
|
664
652
|
fontWeight: string | number;
|
|
665
653
|
letterSpacing: number;
|
|
666
|
-
wordSpacing: number;
|
|
667
654
|
lineHeight: number;
|
|
668
655
|
textTransform: "none" | "uppercase" | "lowercase" | "capitalize";
|
|
669
656
|
pauseThreshold: number;
|
|
@@ -745,7 +732,6 @@ type AnimationStyle = "karaoke" | "highlight" | "pop" | "fade" | "slide" | "boun
|
|
|
745
732
|
type AnimationDirection = "left" | "right" | "up" | "down";
|
|
746
733
|
interface WordAnimationConfig {
|
|
747
734
|
style: AnimationStyle;
|
|
748
|
-
speed: number;
|
|
749
735
|
direction: AnimationDirection;
|
|
750
736
|
}
|
|
751
737
|
interface WordAnimationState {
|
|
@@ -1183,7 +1169,7 @@ interface FrameSchedule {
|
|
|
1183
1169
|
uniqueFrameCount: number;
|
|
1184
1170
|
skipRatio: number;
|
|
1185
1171
|
}
|
|
1186
|
-
declare function createFrameSchedule(layout: CaptionLayout, durationMs: number, fps: number, animationStyle?: AnimationStyle
|
|
1172
|
+
declare function createFrameSchedule(layout: CaptionLayout, durationMs: number, fps: number, animationStyle?: AnimationStyle): FrameSchedule;
|
|
1187
1173
|
|
|
1188
1174
|
interface RichCaptionRendererOptions {
|
|
1189
1175
|
width: number;
|
|
@@ -1252,7 +1238,6 @@ declare class RichCaptionRenderer {
|
|
|
1252
1238
|
private mapVerticalAlign;
|
|
1253
1239
|
private mapHorizontalAlign;
|
|
1254
1240
|
private extractAnimationStyle;
|
|
1255
|
-
private extractAnimationSpeed;
|
|
1256
1241
|
private logProgress;
|
|
1257
1242
|
private logCompletion;
|
|
1258
1243
|
private checkMemoryUsage;
|
package/dist/entry.node.js
CHANGED
|
@@ -18,8 +18,7 @@ import {
|
|
|
18
18
|
svgShadowSchema,
|
|
19
19
|
svgTransformSchema,
|
|
20
20
|
svgGradientStopSchema,
|
|
21
|
-
richCaptionActiveSchema as baseCaptionActiveSchema
|
|
22
|
-
richCaptionWordAnimationSchema as baseCaptionWordAnimationSchema
|
|
21
|
+
richCaptionActiveSchema as baseCaptionActiveSchema
|
|
23
22
|
} from "@shotstack/schemas/zod";
|
|
24
23
|
|
|
25
24
|
// src/config/canvas-constants.ts
|
|
@@ -188,8 +187,7 @@ var richCaptionFontSchema = z.object({
|
|
|
188
187
|
weight: z.union([z.string(), z.number()]).default("400"),
|
|
189
188
|
color: z.string().regex(HEX6).default("#ffffff"),
|
|
190
189
|
opacity: z.number().min(0).max(1).default(1),
|
|
191
|
-
background: z.string().regex(HEX6).optional()
|
|
192
|
-
textDecoration: z.enum(["none", "underline", "line-through"]).default("none")
|
|
190
|
+
background: z.string().regex(HEX6).optional()
|
|
193
191
|
});
|
|
194
192
|
var richCaptionActiveSchema = baseCaptionActiveSchema.extend({
|
|
195
193
|
font: z.object({
|
|
@@ -198,23 +196,28 @@ var richCaptionActiveSchema = baseCaptionActiveSchema.extend({
|
|
|
198
196
|
opacity: z.number().min(0).max(1).optional(),
|
|
199
197
|
textDecoration: z.enum(["none", "underline", "line-through"]).optional()
|
|
200
198
|
}).optional(),
|
|
201
|
-
stroke: z.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
199
|
+
stroke: z.union([
|
|
200
|
+
z.object({
|
|
201
|
+
width: z.number().min(0).optional(),
|
|
202
|
+
color: z.string().regex(HEX6).optional(),
|
|
203
|
+
opacity: z.number().min(0).max(1).optional()
|
|
204
|
+
}),
|
|
205
|
+
z.literal("none")
|
|
206
|
+
]).optional(),
|
|
207
|
+
shadow: z.union([
|
|
208
|
+
z.object({
|
|
209
|
+
offsetX: z.number().optional(),
|
|
210
|
+
offsetY: z.number().optional(),
|
|
211
|
+
blur: z.number().min(0).optional(),
|
|
212
|
+
color: z.string().regex(HEX6).optional(),
|
|
213
|
+
opacity: z.number().min(0).max(1).optional()
|
|
214
|
+
}),
|
|
215
|
+
z.literal("none")
|
|
216
|
+
]).optional(),
|
|
213
217
|
scale: z.number().min(0.5).max(2).default(1)
|
|
214
218
|
});
|
|
215
|
-
var richCaptionWordAnimationSchema =
|
|
219
|
+
var richCaptionWordAnimationSchema = z.object({
|
|
216
220
|
style: z.enum(["karaoke", "highlight", "pop", "fade", "slide", "bounce", "typewriter", "none"]).default("highlight"),
|
|
217
|
-
speed: z.number().min(0.5).max(2).default(1),
|
|
218
221
|
direction: z.enum(["left", "right", "up", "down"]).default("up")
|
|
219
222
|
});
|
|
220
223
|
var richCaptionAssetSchema = z.object({
|
|
@@ -1131,7 +1134,7 @@ var LayoutEngine = class {
|
|
|
1131
1134
|
}
|
|
1132
1135
|
async layout(params) {
|
|
1133
1136
|
try {
|
|
1134
|
-
const { textTransform, desc, fontSize, letterSpacing, width, emojiFallback } = params;
|
|
1137
|
+
const { textTransform, desc, fontSize, letterSpacing, wordSpacing = 0, width, emojiFallback } = params;
|
|
1135
1138
|
const input = this.transformText(params.text, textTransform);
|
|
1136
1139
|
if (!input || input.length === 0) {
|
|
1137
1140
|
return [];
|
|
@@ -1161,9 +1164,10 @@ var LayoutEngine = class {
|
|
|
1161
1164
|
char = String.fromCodePoint(codePoint);
|
|
1162
1165
|
}
|
|
1163
1166
|
}
|
|
1167
|
+
const isSpace = char === " ";
|
|
1164
1168
|
return {
|
|
1165
1169
|
id: g.g,
|
|
1166
|
-
xAdvance: g.ax * scale + letterSpacing,
|
|
1170
|
+
xAdvance: g.ax * scale + letterSpacing + (isSpace ? wordSpacing : 0),
|
|
1167
1171
|
xOffset: g.dx * scale,
|
|
1168
1172
|
yOffset: -g.dy * scale,
|
|
1169
1173
|
cluster: g.cl,
|
|
@@ -2440,10 +2444,10 @@ var CaptionLayoutEngine = class {
|
|
|
2440
2444
|
let spaceWidth;
|
|
2441
2445
|
if (config.measureTextWidth) {
|
|
2442
2446
|
const fontString = `${config.fontWeight} ${config.fontSize}px "${config.fontFamily}"`;
|
|
2443
|
-
spaceWidth = config.measureTextWidth(" ", fontString)
|
|
2447
|
+
spaceWidth = config.measureTextWidth(" ", fontString);
|
|
2444
2448
|
} else {
|
|
2445
2449
|
const spaceWord = await this.measureWord(" ", measurementConfig);
|
|
2446
|
-
spaceWidth = spaceWord.width
|
|
2450
|
+
spaceWidth = spaceWord.width;
|
|
2447
2451
|
}
|
|
2448
2452
|
const groups = wordGroups.flatMap((indices) => {
|
|
2449
2453
|
const groupWidths = indices.map((i) => store.widths[i]);
|
|
@@ -2642,12 +2646,8 @@ function calculateWordProgress(ctx) {
|
|
|
2642
2646
|
function isWordActive(ctx) {
|
|
2643
2647
|
return ctx.currentTime >= ctx.wordStart && ctx.currentTime < ctx.wordEnd;
|
|
2644
2648
|
}
|
|
2645
|
-
function calculateKaraokeState(ctx
|
|
2649
|
+
function calculateKaraokeState(ctx) {
|
|
2646
2650
|
const isActive = isWordActive(ctx);
|
|
2647
|
-
const wordDuration = ctx.wordEnd - ctx.wordStart;
|
|
2648
|
-
const adjustedDuration = wordDuration / speed;
|
|
2649
|
-
const adjustedEnd = ctx.wordStart + adjustedDuration;
|
|
2650
|
-
const adjustedCtx = { ...ctx, wordEnd: adjustedEnd };
|
|
2651
2651
|
if (ctx.currentTime < ctx.wordStart) {
|
|
2652
2652
|
return {
|
|
2653
2653
|
fillProgress: 0,
|
|
@@ -2655,7 +2655,7 @@ function calculateKaraokeState(ctx, speed) {
|
|
|
2655
2655
|
opacity: 1
|
|
2656
2656
|
};
|
|
2657
2657
|
}
|
|
2658
|
-
if (ctx.currentTime >=
|
|
2658
|
+
if (ctx.currentTime >= ctx.wordEnd) {
|
|
2659
2659
|
return {
|
|
2660
2660
|
fillProgress: 1,
|
|
2661
2661
|
isActive: false,
|
|
@@ -2663,7 +2663,7 @@ function calculateKaraokeState(ctx, speed) {
|
|
|
2663
2663
|
};
|
|
2664
2664
|
}
|
|
2665
2665
|
return {
|
|
2666
|
-
fillProgress: calculateWordProgress(
|
|
2666
|
+
fillProgress: calculateWordProgress(ctx),
|
|
2667
2667
|
isActive,
|
|
2668
2668
|
opacity: 1
|
|
2669
2669
|
};
|
|
@@ -2676,7 +2676,7 @@ function calculateHighlightState(ctx) {
|
|
|
2676
2676
|
opacity: 1
|
|
2677
2677
|
};
|
|
2678
2678
|
}
|
|
2679
|
-
function calculatePopState(ctx, activeScale
|
|
2679
|
+
function calculatePopState(ctx, activeScale) {
|
|
2680
2680
|
if (ctx.currentTime < ctx.wordStart) {
|
|
2681
2681
|
return {
|
|
2682
2682
|
scale: 0.5,
|
|
@@ -2685,9 +2685,7 @@ function calculatePopState(ctx, activeScale, speed) {
|
|
|
2685
2685
|
fillProgress: 0
|
|
2686
2686
|
};
|
|
2687
2687
|
}
|
|
2688
|
-
const
|
|
2689
|
-
const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
|
|
2690
|
-
const progress = calculateAnimationProgress(adjustedCtx);
|
|
2688
|
+
const progress = calculateAnimationProgress(ctx);
|
|
2691
2689
|
const easedProgress = easeOutBack(progress);
|
|
2692
2690
|
const startScale = 0.5;
|
|
2693
2691
|
const isActive = isWordActive(ctx);
|
|
@@ -2700,7 +2698,7 @@ function calculatePopState(ctx, activeScale, speed) {
|
|
|
2700
2698
|
fillProgress: isActive ? 1 : 0
|
|
2701
2699
|
};
|
|
2702
2700
|
}
|
|
2703
|
-
function calculateFadeState(ctx
|
|
2701
|
+
function calculateFadeState(ctx) {
|
|
2704
2702
|
if (ctx.currentTime < ctx.wordStart) {
|
|
2705
2703
|
return {
|
|
2706
2704
|
opacity: 0,
|
|
@@ -2708,9 +2706,7 @@ function calculateFadeState(ctx, speed) {
|
|
|
2708
2706
|
fillProgress: 0
|
|
2709
2707
|
};
|
|
2710
2708
|
}
|
|
2711
|
-
const
|
|
2712
|
-
const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
|
|
2713
|
-
const progress = calculateAnimationProgress(adjustedCtx);
|
|
2709
|
+
const progress = calculateAnimationProgress(ctx);
|
|
2714
2710
|
const easedProgress = easeInOutQuad(progress);
|
|
2715
2711
|
const isActive = isWordActive(ctx);
|
|
2716
2712
|
return {
|
|
@@ -2719,7 +2715,7 @@ function calculateFadeState(ctx, speed) {
|
|
|
2719
2715
|
fillProgress: isActive ? 1 : 0
|
|
2720
2716
|
};
|
|
2721
2717
|
}
|
|
2722
|
-
function calculateSlideState(ctx, direction,
|
|
2718
|
+
function calculateSlideState(ctx, direction, fontSize) {
|
|
2723
2719
|
const slideDistance = fontSize * 1.5;
|
|
2724
2720
|
if (ctx.currentTime < ctx.wordStart) {
|
|
2725
2721
|
const offset2 = getDirectionOffset(direction, slideDistance);
|
|
@@ -2731,9 +2727,7 @@ function calculateSlideState(ctx, direction, speed, fontSize) {
|
|
|
2731
2727
|
fillProgress: 0
|
|
2732
2728
|
};
|
|
2733
2729
|
}
|
|
2734
|
-
const
|
|
2735
|
-
const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
|
|
2736
|
-
const progress = calculateAnimationProgress(adjustedCtx);
|
|
2730
|
+
const progress = calculateAnimationProgress(ctx);
|
|
2737
2731
|
const easedProgress = easeOutCirc(progress);
|
|
2738
2732
|
const offset = getDirectionOffset(direction, slideDistance);
|
|
2739
2733
|
const translateX = offset.x * (1 - easedProgress);
|
|
@@ -2759,7 +2753,7 @@ function getDirectionOffset(direction, distance) {
|
|
|
2759
2753
|
return { x: 0, y: -distance };
|
|
2760
2754
|
}
|
|
2761
2755
|
}
|
|
2762
|
-
function calculateBounceState(ctx,
|
|
2756
|
+
function calculateBounceState(ctx, fontSize) {
|
|
2763
2757
|
const bounceDistance = fontSize * 0.8;
|
|
2764
2758
|
if (ctx.currentTime < ctx.wordStart) {
|
|
2765
2759
|
return {
|
|
@@ -2769,9 +2763,7 @@ function calculateBounceState(ctx, speed, fontSize) {
|
|
|
2769
2763
|
fillProgress: 0
|
|
2770
2764
|
};
|
|
2771
2765
|
}
|
|
2772
|
-
const
|
|
2773
|
-
const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
|
|
2774
|
-
const progress = calculateAnimationProgress(adjustedCtx);
|
|
2766
|
+
const progress = calculateAnimationProgress(ctx);
|
|
2775
2767
|
const easedProgress = easeOutBounce(progress);
|
|
2776
2768
|
const isActive = isWordActive(ctx);
|
|
2777
2769
|
return {
|
|
@@ -2781,11 +2773,7 @@ function calculateBounceState(ctx, speed, fontSize) {
|
|
|
2781
2773
|
fillProgress: isActive ? 1 : 0
|
|
2782
2774
|
};
|
|
2783
2775
|
}
|
|
2784
|
-
function calculateTypewriterState(ctx, charCount
|
|
2785
|
-
const wordDuration = ctx.wordEnd - ctx.wordStart;
|
|
2786
|
-
const adjustedDuration = wordDuration / speed;
|
|
2787
|
-
const adjustedEnd = ctx.wordStart + adjustedDuration;
|
|
2788
|
-
const adjustedCtx = { ...ctx, wordEnd: adjustedEnd };
|
|
2776
|
+
function calculateTypewriterState(ctx, charCount) {
|
|
2789
2777
|
if (ctx.currentTime < ctx.wordStart) {
|
|
2790
2778
|
return {
|
|
2791
2779
|
visibleCharacters: 0,
|
|
@@ -2794,7 +2782,7 @@ function calculateTypewriterState(ctx, charCount, speed) {
|
|
|
2794
2782
|
fillProgress: 0
|
|
2795
2783
|
};
|
|
2796
2784
|
}
|
|
2797
|
-
if (ctx.currentTime >=
|
|
2785
|
+
if (ctx.currentTime >= ctx.wordEnd) {
|
|
2798
2786
|
return {
|
|
2799
2787
|
visibleCharacters: charCount,
|
|
2800
2788
|
opacity: 1,
|
|
@@ -2802,7 +2790,7 @@ function calculateTypewriterState(ctx, charCount, speed) {
|
|
|
2802
2790
|
fillProgress: 0
|
|
2803
2791
|
};
|
|
2804
2792
|
}
|
|
2805
|
-
const progress = calculateWordProgress(
|
|
2793
|
+
const progress = calculateWordProgress(ctx);
|
|
2806
2794
|
const visibleCharacters = Math.ceil(progress * charCount);
|
|
2807
2795
|
const isActive = isWordActive(ctx);
|
|
2808
2796
|
return {
|
|
@@ -2820,7 +2808,6 @@ function calculateNoneState(_ctx) {
|
|
|
2820
2808
|
};
|
|
2821
2809
|
}
|
|
2822
2810
|
function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, activeScale = 1, charCount = 0, fontSize = 48, isRTL = false) {
|
|
2823
|
-
const safeSpeed = config.speed > 0 ? config.speed : 1;
|
|
2824
2811
|
const ctx = {
|
|
2825
2812
|
wordStart,
|
|
2826
2813
|
wordEnd,
|
|
@@ -2831,27 +2818,27 @@ function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, ac
|
|
|
2831
2818
|
let partialState;
|
|
2832
2819
|
switch (config.style) {
|
|
2833
2820
|
case "karaoke":
|
|
2834
|
-
partialState = calculateKaraokeState(ctx
|
|
2821
|
+
partialState = calculateKaraokeState(ctx);
|
|
2835
2822
|
break;
|
|
2836
2823
|
case "highlight":
|
|
2837
2824
|
partialState = calculateHighlightState(ctx);
|
|
2838
2825
|
break;
|
|
2839
2826
|
case "pop":
|
|
2840
|
-
partialState = calculatePopState(ctx, activeScale
|
|
2827
|
+
partialState = calculatePopState(ctx, activeScale);
|
|
2841
2828
|
break;
|
|
2842
2829
|
case "fade":
|
|
2843
|
-
partialState = calculateFadeState(ctx
|
|
2830
|
+
partialState = calculateFadeState(ctx);
|
|
2844
2831
|
break;
|
|
2845
2832
|
case "slide": {
|
|
2846
2833
|
const slideDir = mirrorAnimationDirection(config.direction, isRTL);
|
|
2847
|
-
partialState = calculateSlideState(ctx, slideDir,
|
|
2834
|
+
partialState = calculateSlideState(ctx, slideDir, fontSize);
|
|
2848
2835
|
break;
|
|
2849
2836
|
}
|
|
2850
2837
|
case "bounce":
|
|
2851
|
-
partialState = calculateBounceState(ctx,
|
|
2838
|
+
partialState = calculateBounceState(ctx, fontSize);
|
|
2852
2839
|
break;
|
|
2853
2840
|
case "typewriter":
|
|
2854
|
-
partialState = calculateTypewriterState(ctx, charCount
|
|
2841
|
+
partialState = calculateTypewriterState(ctx, charCount);
|
|
2855
2842
|
break;
|
|
2856
2843
|
case "none":
|
|
2857
2844
|
default:
|
|
@@ -2884,7 +2871,6 @@ function calculateAnimationStatesForGroup(words, currentTime, config, activeScal
|
|
|
2884
2871
|
function getDefaultAnimationConfig() {
|
|
2885
2872
|
return {
|
|
2886
2873
|
style: "highlight",
|
|
2887
|
-
speed: 1,
|
|
2888
2874
|
direction: "up"
|
|
2889
2875
|
};
|
|
2890
2876
|
}
|
|
@@ -2925,18 +2911,17 @@ function extractFontConfig(asset) {
|
|
|
2925
2911
|
function extractStrokeConfig(asset, isActive) {
|
|
2926
2912
|
const baseStroke = asset.stroke;
|
|
2927
2913
|
const activeStroke = asset.active?.stroke;
|
|
2928
|
-
if (!baseStroke && !activeStroke) {
|
|
2929
|
-
return void 0;
|
|
2930
|
-
}
|
|
2931
2914
|
if (isActive) {
|
|
2932
|
-
if (
|
|
2915
|
+
if (activeStroke === "none") {
|
|
2933
2916
|
return void 0;
|
|
2934
2917
|
}
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2918
|
+
if (activeStroke && typeof activeStroke === "object") {
|
|
2919
|
+
return {
|
|
2920
|
+
width: activeStroke.width ?? baseStroke?.width ?? 0,
|
|
2921
|
+
color: activeStroke.color ?? baseStroke?.color ?? "#000000",
|
|
2922
|
+
opacity: activeStroke.opacity ?? baseStroke?.opacity ?? 1
|
|
2923
|
+
};
|
|
2924
|
+
}
|
|
2940
2925
|
}
|
|
2941
2926
|
if (baseStroke) {
|
|
2942
2927
|
return {
|
|
@@ -2950,20 +2935,19 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
2950
2935
|
function extractShadowConfig(asset, isActive) {
|
|
2951
2936
|
const baseShadow = asset.shadow;
|
|
2952
2937
|
const activeShadow = asset.active?.shadow;
|
|
2953
|
-
if (!baseShadow && !activeShadow) {
|
|
2954
|
-
return void 0;
|
|
2955
|
-
}
|
|
2956
2938
|
if (isActive) {
|
|
2957
|
-
if (
|
|
2939
|
+
if (activeShadow === "none") {
|
|
2958
2940
|
return void 0;
|
|
2959
2941
|
}
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2942
|
+
if (activeShadow && typeof activeShadow === "object") {
|
|
2943
|
+
return {
|
|
2944
|
+
offsetX: activeShadow.offsetX ?? baseShadow?.offsetX ?? 0,
|
|
2945
|
+
offsetY: activeShadow.offsetY ?? baseShadow?.offsetY ?? 0,
|
|
2946
|
+
blur: activeShadow.blur ?? baseShadow?.blur ?? 0,
|
|
2947
|
+
color: activeShadow.color ?? baseShadow?.color ?? "#000000",
|
|
2948
|
+
opacity: activeShadow.opacity ?? baseShadow?.opacity ?? 0.5
|
|
2949
|
+
};
|
|
2950
|
+
}
|
|
2967
2951
|
}
|
|
2968
2952
|
if (baseShadow) {
|
|
2969
2953
|
return {
|
|
@@ -2979,7 +2963,12 @@ function extractShadowConfig(asset, isActive) {
|
|
|
2979
2963
|
function extractBackgroundConfig(asset, isActive, fontSize) {
|
|
2980
2964
|
const fontBackground = asset.font?.background;
|
|
2981
2965
|
const activeBackground = asset.active?.font?.background;
|
|
2982
|
-
|
|
2966
|
+
let bgColor;
|
|
2967
|
+
if (isActive) {
|
|
2968
|
+
bgColor = activeBackground ?? fontBackground;
|
|
2969
|
+
} else {
|
|
2970
|
+
bgColor = fontBackground;
|
|
2971
|
+
}
|
|
2983
2972
|
if (!bgColor) {
|
|
2984
2973
|
return void 0;
|
|
2985
2974
|
}
|
|
@@ -3030,7 +3019,7 @@ function extractCaptionBorder(asset) {
|
|
|
3030
3019
|
};
|
|
3031
3020
|
}
|
|
3032
3021
|
function extractTextDecoration(asset, isActive) {
|
|
3033
|
-
const baseDecoration = asset.
|
|
3022
|
+
const baseDecoration = asset.style?.textDecoration;
|
|
3034
3023
|
const activeDecoration = asset.active?.font?.textDecoration;
|
|
3035
3024
|
if (isActive && activeDecoration !== void 0) {
|
|
3036
3025
|
return activeDecoration === "none" ? void 0 : activeDecoration;
|
|
@@ -3047,7 +3036,6 @@ function extractAnimationConfig(asset) {
|
|
|
3047
3036
|
}
|
|
3048
3037
|
return {
|
|
3049
3038
|
style: wordAnim.style ?? "highlight",
|
|
3050
|
-
speed: wordAnim.speed ?? 1,
|
|
3051
3039
|
direction: wordAnim.direction ?? "up"
|
|
3052
3040
|
};
|
|
3053
3041
|
}
|
|
@@ -5094,11 +5082,14 @@ function extractSvgDimensions(svgString) {
|
|
|
5094
5082
|
}
|
|
5095
5083
|
|
|
5096
5084
|
// src/core/canvas-text-measurer.ts
|
|
5097
|
-
async function createCanvasTextMeasurer() {
|
|
5085
|
+
async function createCanvasTextMeasurer(letterSpacing) {
|
|
5098
5086
|
const canvasMod = await import("canvas");
|
|
5099
5087
|
const canvas = canvasMod.createCanvas(1, 1);
|
|
5100
5088
|
const ctx = canvas.getContext("2d");
|
|
5101
5089
|
ctx.textBaseline = "alphabetic";
|
|
5090
|
+
if (letterSpacing) {
|
|
5091
|
+
ctx.letterSpacing = `${letterSpacing}px`;
|
|
5092
|
+
}
|
|
5102
5093
|
let lastFont = "";
|
|
5103
5094
|
return (text, font) => {
|
|
5104
5095
|
if (font !== lastFont) {
|
|
@@ -5467,7 +5458,7 @@ function findActiveWordIndex(store, groupWordIndices, timeMs) {
|
|
|
5467
5458
|
}
|
|
5468
5459
|
return -1;
|
|
5469
5460
|
}
|
|
5470
|
-
function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle
|
|
5461
|
+
function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle) {
|
|
5471
5462
|
if (groupWordIndices.length === 0) {
|
|
5472
5463
|
return "idle";
|
|
5473
5464
|
}
|
|
@@ -5484,7 +5475,7 @@ function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle, spee
|
|
|
5484
5475
|
return "after";
|
|
5485
5476
|
}
|
|
5486
5477
|
if (TRANSITION_ANIMATION_STYLES.has(animationStyle)) {
|
|
5487
|
-
const transitionDurationMs =
|
|
5478
|
+
const transitionDurationMs = ANIMATION_DURATION_MS[animationStyle] ?? 200;
|
|
5488
5479
|
for (const idx of groupWordIndices) {
|
|
5489
5480
|
const wordStart = store.startTimes[idx];
|
|
5490
5481
|
if (timeMs >= wordStart && timeMs < wordStart + transitionDurationMs) {
|
|
@@ -5506,7 +5497,7 @@ function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle, spee
|
|
|
5506
5497
|
}
|
|
5507
5498
|
return "before";
|
|
5508
5499
|
}
|
|
5509
|
-
function computeStateSignature(layout, timeMs, animationStyle
|
|
5500
|
+
function computeStateSignature(layout, timeMs, animationStyle) {
|
|
5510
5501
|
const groupIndex = findGroupIndexAtTime(layout.groups, timeMs);
|
|
5511
5502
|
if (groupIndex === -1) {
|
|
5512
5503
|
return { groupIndex: -1, activeWordIndex: -1, animationPhase: "idle" };
|
|
@@ -5517,21 +5508,20 @@ function computeStateSignature(layout, timeMs, animationStyle, speed) {
|
|
|
5517
5508
|
layout.store,
|
|
5518
5509
|
group.wordIndices,
|
|
5519
5510
|
timeMs,
|
|
5520
|
-
animationStyle
|
|
5521
|
-
speed
|
|
5511
|
+
animationStyle
|
|
5522
5512
|
);
|
|
5523
5513
|
return { groupIndex, activeWordIndex, animationPhase };
|
|
5524
5514
|
}
|
|
5525
5515
|
function signaturesMatch(a, b) {
|
|
5526
5516
|
return a.groupIndex === b.groupIndex && a.activeWordIndex === b.activeWordIndex && a.animationPhase === b.animationPhase;
|
|
5527
5517
|
}
|
|
5528
|
-
function createFrameSchedule(layout, durationMs, fps, animationStyle = "highlight"
|
|
5518
|
+
function createFrameSchedule(layout, durationMs, fps, animationStyle = "highlight") {
|
|
5529
5519
|
const totalFrames = Math.max(2, Math.round(durationMs / 1e3 * fps) + 1);
|
|
5530
5520
|
const renderFrames = [];
|
|
5531
5521
|
let previousSignature = null;
|
|
5532
5522
|
for (let frame = 0; frame < totalFrames; frame++) {
|
|
5533
5523
|
const timeMs = frame / (totalFrames - 1) * durationMs;
|
|
5534
|
-
const signature = computeStateSignature(layout, timeMs, animationStyle
|
|
5524
|
+
const signature = computeStateSignature(layout, timeMs, animationStyle);
|
|
5535
5525
|
const isAnimating = signature.animationPhase === "animating";
|
|
5536
5526
|
if (isAnimating || previousSignature === null || !signaturesMatch(signature, previousSignature)) {
|
|
5537
5527
|
renderFrames.push({
|
|
@@ -5878,7 +5868,7 @@ var RichCaptionRenderer = class {
|
|
|
5878
5868
|
}
|
|
5879
5869
|
const font = asset.font;
|
|
5880
5870
|
const style = asset.style;
|
|
5881
|
-
const measureTextWidth = await createCanvasTextMeasurer();
|
|
5871
|
+
const measureTextWidth = await createCanvasTextMeasurer(style?.letterSpacing ?? 0);
|
|
5882
5872
|
const fontSize = font?.size ?? 24;
|
|
5883
5873
|
const lineHeight = style?.lineHeight ?? 1.2;
|
|
5884
5874
|
const padding = extractCaptionPadding(asset);
|
|
@@ -5899,7 +5889,6 @@ var RichCaptionRenderer = class {
|
|
|
5899
5889
|
fontFamily: font?.family ?? "Roboto",
|
|
5900
5890
|
fontWeight: String(font?.weight ?? "400"),
|
|
5901
5891
|
letterSpacing: style?.letterSpacing ?? 0,
|
|
5902
|
-
wordSpacing: typeof style?.wordSpacing === "number" ? style.wordSpacing : 0,
|
|
5903
5892
|
lineHeight,
|
|
5904
5893
|
textTransform: style?.textTransform ?? "none",
|
|
5905
5894
|
pauseThreshold: this.currentAsset?.pauseThreshold ?? 500,
|
|
@@ -5933,14 +5922,12 @@ var RichCaptionRenderer = class {
|
|
|
5933
5922
|
throw new Error("No asset loaded. Call loadAsset() first.");
|
|
5934
5923
|
}
|
|
5935
5924
|
const animationStyle = this.extractAnimationStyle();
|
|
5936
|
-
const animationSpeed = this.extractAnimationSpeed();
|
|
5937
5925
|
const durationMs = duration * 1e3;
|
|
5938
5926
|
const schedule = createFrameSchedule(
|
|
5939
5927
|
this.currentLayout,
|
|
5940
5928
|
durationMs,
|
|
5941
5929
|
this.fps,
|
|
5942
|
-
animationStyle
|
|
5943
|
-
animationSpeed
|
|
5930
|
+
animationStyle
|
|
5944
5931
|
);
|
|
5945
5932
|
const bgColor = options?.bgColor;
|
|
5946
5933
|
const hasAlpha = !bgColor;
|
|
@@ -6101,13 +6088,11 @@ var RichCaptionRenderer = class {
|
|
|
6101
6088
|
throw new Error("No asset loaded. Call loadAsset() first.");
|
|
6102
6089
|
}
|
|
6103
6090
|
const animationStyle = this.extractAnimationStyle();
|
|
6104
|
-
const animationSpeed = this.extractAnimationSpeed();
|
|
6105
6091
|
return createFrameSchedule(
|
|
6106
6092
|
this.currentLayout,
|
|
6107
6093
|
duration * 1e3,
|
|
6108
6094
|
this.fps,
|
|
6109
|
-
animationStyle
|
|
6110
|
-
animationSpeed
|
|
6095
|
+
animationStyle
|
|
6111
6096
|
);
|
|
6112
6097
|
}
|
|
6113
6098
|
getStats() {
|
|
@@ -6145,10 +6130,6 @@ var RichCaptionRenderer = class {
|
|
|
6145
6130
|
const wordAnim = this.currentAsset?.wordAnimation;
|
|
6146
6131
|
return wordAnim?.style ?? "highlight";
|
|
6147
6132
|
}
|
|
6148
|
-
extractAnimationSpeed() {
|
|
6149
|
-
const wordAnim = this.currentAsset?.wordAnimation;
|
|
6150
|
-
return wordAnim?.speed ?? 1;
|
|
6151
|
-
}
|
|
6152
6133
|
logProgress(pct, framesProcessed, totalFrames, uniqueProcessed, uniqueTotal, fps, eta) {
|
|
6153
6134
|
if (typeof process !== "undefined" && process.stderr) {
|
|
6154
6135
|
process.stderr.write(
|
|
@@ -6461,6 +6442,7 @@ async function createTextEngine(opts = {}) {
|
|
|
6461
6442
|
text: asset.text,
|
|
6462
6443
|
width: (asset.width ?? width) - padding2.left - padding2.right,
|
|
6463
6444
|
letterSpacing: asset.style?.letterSpacing ?? 0,
|
|
6445
|
+
wordSpacing: typeof asset.style?.wordSpacing === "number" ? asset.style.wordSpacing : 0,
|
|
6464
6446
|
fontSize: main.size,
|
|
6465
6447
|
lineHeight: asset.style?.lineHeight ?? 1.2,
|
|
6466
6448
|
desc,
|