@shotstack/shotstack-canvas 2.1.1 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entry.node.cjs +90 -91
- package/dist/entry.node.js +90 -91
- package/dist/entry.web.js +90 -91
- package/package.json +5 -2
package/dist/entry.node.cjs
CHANGED
|
@@ -2842,10 +2842,11 @@ function calculateTypewriterState(ctx, charCount, speed) {
|
|
|
2842
2842
|
isActive: isWordActive(ctx)
|
|
2843
2843
|
};
|
|
2844
2844
|
}
|
|
2845
|
-
function calculateNoneState(
|
|
2845
|
+
function calculateNoneState(_ctx) {
|
|
2846
2846
|
return {
|
|
2847
2847
|
opacity: 1,
|
|
2848
|
-
isActive:
|
|
2848
|
+
isActive: false,
|
|
2849
|
+
fillProgress: 0
|
|
2849
2850
|
};
|
|
2850
2851
|
}
|
|
2851
2852
|
function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, activeScale = 1, charCount = 0, fontSize = 48, isRTL = false) {
|
|
@@ -2888,7 +2889,7 @@ function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, ac
|
|
|
2888
2889
|
break;
|
|
2889
2890
|
}
|
|
2890
2891
|
const mergedState = { ...baseState, ...partialState };
|
|
2891
|
-
if (mergedState.isActive && activeScale !== 1 && config.style !== "pop") {
|
|
2892
|
+
if (mergedState.isActive && activeScale !== 1 && config.style !== "pop" && config.style !== "none") {
|
|
2892
2893
|
mergedState.scale = activeScale;
|
|
2893
2894
|
}
|
|
2894
2895
|
return mergedState;
|
|
@@ -2950,7 +2951,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
2950
2951
|
if (!baseStroke && !activeStroke) {
|
|
2951
2952
|
return void 0;
|
|
2952
2953
|
}
|
|
2953
|
-
if (isActive
|
|
2954
|
+
if (isActive) {
|
|
2955
|
+
if (!activeStroke) {
|
|
2956
|
+
return void 0;
|
|
2957
|
+
}
|
|
2954
2958
|
return {
|
|
2955
2959
|
width: activeStroke.width ?? baseStroke?.width ?? 0,
|
|
2956
2960
|
color: activeStroke.color ?? baseStroke?.color ?? "#000000",
|
|
@@ -2966,7 +2970,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
2966
2970
|
}
|
|
2967
2971
|
return void 0;
|
|
2968
2972
|
}
|
|
2969
|
-
function extractShadowConfig(asset) {
|
|
2973
|
+
function extractShadowConfig(asset, isActive) {
|
|
2974
|
+
if (isActive) {
|
|
2975
|
+
return void 0;
|
|
2976
|
+
}
|
|
2970
2977
|
const shadow = asset.shadow;
|
|
2971
2978
|
if (!shadow) {
|
|
2972
2979
|
return void 0;
|
|
@@ -3073,27 +3080,10 @@ function createDrawCaptionWordOp(word, animState, asset, fontConfig) {
|
|
|
3073
3080
|
visibleCharacters: animState.visibleCharacters,
|
|
3074
3081
|
letterSpacing: fontConfig.letterSpacing > 0 ? fontConfig.letterSpacing : void 0,
|
|
3075
3082
|
stroke: extractStrokeConfig(asset, isActive),
|
|
3076
|
-
shadow: extractShadowConfig(asset),
|
|
3083
|
+
shadow: extractShadowConfig(asset, isActive),
|
|
3077
3084
|
background: extractBackgroundConfig(asset, isActive, fontConfig.size)
|
|
3078
3085
|
};
|
|
3079
3086
|
}
|
|
3080
|
-
function calculateGroupBounds(activeGroup, padding, frameWidth, activeScale, fontSize) {
|
|
3081
|
-
let minY = Infinity;
|
|
3082
|
-
let maxY = -Infinity;
|
|
3083
|
-
for (const line of activeGroup.lines) {
|
|
3084
|
-
const lineY = line.y - line.height * ASCENT_RATIO;
|
|
3085
|
-
const lineBottom = line.y + line.height * DESCENT_RATIO;
|
|
3086
|
-
if (lineY < minY) minY = lineY;
|
|
3087
|
-
if (lineBottom > maxY) maxY = lineBottom;
|
|
3088
|
-
}
|
|
3089
|
-
const scaleExpansion = activeScale > 1 ? (activeScale - 1) * fontSize : 0;
|
|
3090
|
-
return {
|
|
3091
|
-
bgX: 0,
|
|
3092
|
-
bgY: minY - padding.top - scaleExpansion,
|
|
3093
|
-
bgWidth: frameWidth,
|
|
3094
|
-
bgHeight: maxY - minY + padding.top + padding.bottom + scaleExpansion * 2
|
|
3095
|
-
};
|
|
3096
|
-
}
|
|
3097
3087
|
function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, config) {
|
|
3098
3088
|
if (layout.store.length === 0) {
|
|
3099
3089
|
return [];
|
|
@@ -3113,48 +3103,35 @@ function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, co
|
|
|
3113
3103
|
fontConfig.size
|
|
3114
3104
|
);
|
|
3115
3105
|
const ops = [];
|
|
3116
|
-
const
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
);
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
op: "RectangleStroke",
|
|
3146
|
-
x: bgX + halfBorder,
|
|
3147
|
-
y: bgY + halfBorder,
|
|
3148
|
-
width: bgWidth - borderConfig.width,
|
|
3149
|
-
height: bgHeight - borderConfig.width,
|
|
3150
|
-
stroke: {
|
|
3151
|
-
width: borderConfig.width,
|
|
3152
|
-
color: borderConfig.color,
|
|
3153
|
-
opacity: borderConfig.opacity
|
|
3154
|
-
},
|
|
3155
|
-
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
3156
|
-
});
|
|
3157
|
-
}
|
|
3106
|
+
const captionBg = extractCaptionBackground(asset);
|
|
3107
|
+
if (captionBg) {
|
|
3108
|
+
ops.push({
|
|
3109
|
+
op: "DrawCaptionBackground",
|
|
3110
|
+
x: 0,
|
|
3111
|
+
y: 0,
|
|
3112
|
+
width: config.frameWidth,
|
|
3113
|
+
height: config.frameHeight,
|
|
3114
|
+
color: captionBg.color,
|
|
3115
|
+
opacity: captionBg.opacity,
|
|
3116
|
+
borderRadius: captionBg.borderRadius
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3119
|
+
const borderConfig = extractCaptionBorder(asset);
|
|
3120
|
+
if (borderConfig) {
|
|
3121
|
+
const halfBorder = borderConfig.width / 2;
|
|
3122
|
+
ops.push({
|
|
3123
|
+
op: "RectangleStroke",
|
|
3124
|
+
x: halfBorder,
|
|
3125
|
+
y: halfBorder,
|
|
3126
|
+
width: config.frameWidth - borderConfig.width,
|
|
3127
|
+
height: config.frameHeight - borderConfig.width,
|
|
3128
|
+
stroke: {
|
|
3129
|
+
width: borderConfig.width,
|
|
3130
|
+
color: borderConfig.color,
|
|
3131
|
+
opacity: borderConfig.opacity
|
|
3132
|
+
},
|
|
3133
|
+
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
3134
|
+
});
|
|
3158
3135
|
}
|
|
3159
3136
|
const nonActiveOps = [];
|
|
3160
3137
|
const activeOps = [];
|
|
@@ -3501,40 +3478,61 @@ async function createNodePainter(opts) {
|
|
|
3501
3478
|
i++;
|
|
3502
3479
|
}
|
|
3503
3480
|
renderToBoth((context) => {
|
|
3504
|
-
|
|
3505
|
-
|
|
3481
|
+
let bgIdx = 0;
|
|
3482
|
+
while (bgIdx < captionWordOps.length) {
|
|
3483
|
+
const wordOp = captionWordOps[bgIdx];
|
|
3484
|
+
if (!wordOp.background) {
|
|
3485
|
+
bgIdx++;
|
|
3486
|
+
continue;
|
|
3487
|
+
}
|
|
3506
3488
|
const wordDisplayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
3507
|
-
if (wordDisplayText.length === 0)
|
|
3489
|
+
if (wordDisplayText.length === 0) {
|
|
3490
|
+
bgIdx++;
|
|
3491
|
+
continue;
|
|
3492
|
+
}
|
|
3493
|
+
const mergeGroup = [wordOp];
|
|
3494
|
+
let nextIdx = bgIdx + 1;
|
|
3495
|
+
while (nextIdx < captionWordOps.length) {
|
|
3496
|
+
const nextWord = captionWordOps[nextIdx];
|
|
3497
|
+
if (!nextWord.background) break;
|
|
3498
|
+
const nextDisplay = getVisibleText(nextWord.text, nextWord.visibleCharacters, nextWord.isRTL);
|
|
3499
|
+
if (nextDisplay.length === 0) break;
|
|
3500
|
+
if (Math.round(nextWord.y) !== Math.round(wordOp.y)) break;
|
|
3501
|
+
if (nextWord.background.color !== wordOp.background.color) break;
|
|
3502
|
+
mergeGroup.push(nextWord);
|
|
3503
|
+
nextIdx++;
|
|
3504
|
+
}
|
|
3505
|
+
const firstWord = mergeGroup[0];
|
|
3506
|
+
const lastWord = mergeGroup[mergeGroup.length - 1];
|
|
3507
|
+
const firstBg = firstWord.background;
|
|
3508
|
+
const mergedLeft = firstWord.x + firstWord.transform.translateX;
|
|
3509
|
+
const mergedRight = lastWord.x + lastWord.transform.translateX + lastWord.width;
|
|
3510
|
+
const mergedWidth = mergedRight - mergedLeft;
|
|
3508
3511
|
context.save();
|
|
3509
|
-
const bgTx = Math.round(
|
|
3510
|
-
const bgTy = Math.round(
|
|
3512
|
+
const bgTx = Math.round(mergedLeft);
|
|
3513
|
+
const bgTy = Math.round(firstWord.y + firstWord.transform.translateY);
|
|
3511
3514
|
context.translate(bgTx, bgTy);
|
|
3512
|
-
if (
|
|
3513
|
-
const halfWidth =
|
|
3515
|
+
if (firstWord.transform.scale !== 1) {
|
|
3516
|
+
const halfWidth = mergedWidth / 2;
|
|
3514
3517
|
context.translate(halfWidth, 0);
|
|
3515
|
-
context.scale(
|
|
3518
|
+
context.scale(firstWord.transform.scale, firstWord.transform.scale);
|
|
3516
3519
|
context.translate(-halfWidth, 0);
|
|
3517
3520
|
}
|
|
3518
|
-
context.globalAlpha =
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
if (wordOp.letterSpacing) {
|
|
3522
|
-
context.letterSpacing = `${wordOp.letterSpacing}px`;
|
|
3523
|
-
}
|
|
3524
|
-
const bgTextWidth = wordOp.width;
|
|
3525
|
-
const bgAscent = wordOp.fontSize * ASCENT_RATIO;
|
|
3526
|
-
const bgDescent = wordOp.fontSize * DESCENT_RATIO;
|
|
3521
|
+
context.globalAlpha = firstWord.transform.opacity;
|
|
3522
|
+
const bgAscent = firstWord.fontSize * ASCENT_RATIO;
|
|
3523
|
+
const bgDescent = firstWord.fontSize * DESCENT_RATIO;
|
|
3527
3524
|
const bgTextHeight = bgAscent + bgDescent;
|
|
3528
|
-
const bgX = -
|
|
3529
|
-
const bgY = -bgAscent -
|
|
3530
|
-
const bgW =
|
|
3531
|
-
const bgH = bgTextHeight +
|
|
3532
|
-
const bgC = parseHex6(
|
|
3525
|
+
const bgX = -firstBg.padding;
|
|
3526
|
+
const bgY = -bgAscent - firstBg.padding;
|
|
3527
|
+
const bgW = mergedWidth + firstBg.padding * 2;
|
|
3528
|
+
const bgH = bgTextHeight + firstBg.padding * 2;
|
|
3529
|
+
const bgC = parseHex6(firstBg.color, firstBg.opacity);
|
|
3533
3530
|
context.fillStyle = `rgba(${bgC.r},${bgC.g},${bgC.b},${bgC.a})`;
|
|
3534
3531
|
context.beginPath();
|
|
3535
|
-
roundRectPath(context, bgX, bgY, bgW, bgH,
|
|
3532
|
+
roundRectPath(context, bgX, bgY, bgW, bgH, firstBg.borderRadius);
|
|
3536
3533
|
context.fill();
|
|
3537
3534
|
context.restore();
|
|
3535
|
+
bgIdx = nextIdx;
|
|
3538
3536
|
}
|
|
3539
3537
|
for (const wordOp of captionWordOps) {
|
|
3540
3538
|
const displayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
@@ -5319,16 +5317,17 @@ var CaptionLayoutEngine = class {
|
|
|
5319
5317
|
});
|
|
5320
5318
|
const contentHeight = config.frameHeight - config.padding.top - config.padding.bottom;
|
|
5321
5319
|
const contentWidth = config.frameWidth - config.padding.left - config.padding.right;
|
|
5320
|
+
const ASCENT_RATIO2 = 0.8;
|
|
5322
5321
|
const calculateGroupY = (group) => {
|
|
5323
5322
|
const totalHeight = group.lines.length * config.fontSize * config.lineHeight;
|
|
5324
5323
|
switch (config.verticalAlign) {
|
|
5325
5324
|
case "top":
|
|
5326
|
-
return config.padding.top + config.fontSize *
|
|
5325
|
+
return config.padding.top + config.fontSize * ASCENT_RATIO2;
|
|
5327
5326
|
case "bottom":
|
|
5328
|
-
return config.frameHeight - config.padding.bottom - totalHeight
|
|
5327
|
+
return config.frameHeight - config.padding.bottom - totalHeight + config.fontSize * ASCENT_RATIO2;
|
|
5329
5328
|
case "middle":
|
|
5330
5329
|
default:
|
|
5331
|
-
return config.padding.top + (contentHeight - totalHeight) / 2 + config.fontSize;
|
|
5330
|
+
return config.padding.top + (contentHeight - totalHeight) / 2 + config.fontSize * ASCENT_RATIO2;
|
|
5332
5331
|
}
|
|
5333
5332
|
};
|
|
5334
5333
|
const allWordTexts = store.words.slice(0, store.length);
|
package/dist/entry.node.js
CHANGED
|
@@ -2439,10 +2439,11 @@ function calculateTypewriterState(ctx, charCount, speed) {
|
|
|
2439
2439
|
isActive: isWordActive(ctx)
|
|
2440
2440
|
};
|
|
2441
2441
|
}
|
|
2442
|
-
function calculateNoneState(
|
|
2442
|
+
function calculateNoneState(_ctx) {
|
|
2443
2443
|
return {
|
|
2444
2444
|
opacity: 1,
|
|
2445
|
-
isActive:
|
|
2445
|
+
isActive: false,
|
|
2446
|
+
fillProgress: 0
|
|
2446
2447
|
};
|
|
2447
2448
|
}
|
|
2448
2449
|
function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, activeScale = 1, charCount = 0, fontSize = 48, isRTL = false) {
|
|
@@ -2485,7 +2486,7 @@ function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, ac
|
|
|
2485
2486
|
break;
|
|
2486
2487
|
}
|
|
2487
2488
|
const mergedState = { ...baseState, ...partialState };
|
|
2488
|
-
if (mergedState.isActive && activeScale !== 1 && config.style !== "pop") {
|
|
2489
|
+
if (mergedState.isActive && activeScale !== 1 && config.style !== "pop" && config.style !== "none") {
|
|
2489
2490
|
mergedState.scale = activeScale;
|
|
2490
2491
|
}
|
|
2491
2492
|
return mergedState;
|
|
@@ -2547,7 +2548,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
2547
2548
|
if (!baseStroke && !activeStroke) {
|
|
2548
2549
|
return void 0;
|
|
2549
2550
|
}
|
|
2550
|
-
if (isActive
|
|
2551
|
+
if (isActive) {
|
|
2552
|
+
if (!activeStroke) {
|
|
2553
|
+
return void 0;
|
|
2554
|
+
}
|
|
2551
2555
|
return {
|
|
2552
2556
|
width: activeStroke.width ?? baseStroke?.width ?? 0,
|
|
2553
2557
|
color: activeStroke.color ?? baseStroke?.color ?? "#000000",
|
|
@@ -2563,7 +2567,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
2563
2567
|
}
|
|
2564
2568
|
return void 0;
|
|
2565
2569
|
}
|
|
2566
|
-
function extractShadowConfig(asset) {
|
|
2570
|
+
function extractShadowConfig(asset, isActive) {
|
|
2571
|
+
if (isActive) {
|
|
2572
|
+
return void 0;
|
|
2573
|
+
}
|
|
2567
2574
|
const shadow = asset.shadow;
|
|
2568
2575
|
if (!shadow) {
|
|
2569
2576
|
return void 0;
|
|
@@ -2670,27 +2677,10 @@ function createDrawCaptionWordOp(word, animState, asset, fontConfig) {
|
|
|
2670
2677
|
visibleCharacters: animState.visibleCharacters,
|
|
2671
2678
|
letterSpacing: fontConfig.letterSpacing > 0 ? fontConfig.letterSpacing : void 0,
|
|
2672
2679
|
stroke: extractStrokeConfig(asset, isActive),
|
|
2673
|
-
shadow: extractShadowConfig(asset),
|
|
2680
|
+
shadow: extractShadowConfig(asset, isActive),
|
|
2674
2681
|
background: extractBackgroundConfig(asset, isActive, fontConfig.size)
|
|
2675
2682
|
};
|
|
2676
2683
|
}
|
|
2677
|
-
function calculateGroupBounds(activeGroup, padding, frameWidth, activeScale, fontSize) {
|
|
2678
|
-
let minY = Infinity;
|
|
2679
|
-
let maxY = -Infinity;
|
|
2680
|
-
for (const line of activeGroup.lines) {
|
|
2681
|
-
const lineY = line.y - line.height * ASCENT_RATIO;
|
|
2682
|
-
const lineBottom = line.y + line.height * DESCENT_RATIO;
|
|
2683
|
-
if (lineY < minY) minY = lineY;
|
|
2684
|
-
if (lineBottom > maxY) maxY = lineBottom;
|
|
2685
|
-
}
|
|
2686
|
-
const scaleExpansion = activeScale > 1 ? (activeScale - 1) * fontSize : 0;
|
|
2687
|
-
return {
|
|
2688
|
-
bgX: 0,
|
|
2689
|
-
bgY: minY - padding.top - scaleExpansion,
|
|
2690
|
-
bgWidth: frameWidth,
|
|
2691
|
-
bgHeight: maxY - minY + padding.top + padding.bottom + scaleExpansion * 2
|
|
2692
|
-
};
|
|
2693
|
-
}
|
|
2694
2684
|
function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, config) {
|
|
2695
2685
|
if (layout.store.length === 0) {
|
|
2696
2686
|
return [];
|
|
@@ -2710,48 +2700,35 @@ function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, co
|
|
|
2710
2700
|
fontConfig.size
|
|
2711
2701
|
);
|
|
2712
2702
|
const ops = [];
|
|
2713
|
-
const
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
);
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
op: "RectangleStroke",
|
|
2743
|
-
x: bgX + halfBorder,
|
|
2744
|
-
y: bgY + halfBorder,
|
|
2745
|
-
width: bgWidth - borderConfig.width,
|
|
2746
|
-
height: bgHeight - borderConfig.width,
|
|
2747
|
-
stroke: {
|
|
2748
|
-
width: borderConfig.width,
|
|
2749
|
-
color: borderConfig.color,
|
|
2750
|
-
opacity: borderConfig.opacity
|
|
2751
|
-
},
|
|
2752
|
-
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
2753
|
-
});
|
|
2754
|
-
}
|
|
2703
|
+
const captionBg = extractCaptionBackground(asset);
|
|
2704
|
+
if (captionBg) {
|
|
2705
|
+
ops.push({
|
|
2706
|
+
op: "DrawCaptionBackground",
|
|
2707
|
+
x: 0,
|
|
2708
|
+
y: 0,
|
|
2709
|
+
width: config.frameWidth,
|
|
2710
|
+
height: config.frameHeight,
|
|
2711
|
+
color: captionBg.color,
|
|
2712
|
+
opacity: captionBg.opacity,
|
|
2713
|
+
borderRadius: captionBg.borderRadius
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
const borderConfig = extractCaptionBorder(asset);
|
|
2717
|
+
if (borderConfig) {
|
|
2718
|
+
const halfBorder = borderConfig.width / 2;
|
|
2719
|
+
ops.push({
|
|
2720
|
+
op: "RectangleStroke",
|
|
2721
|
+
x: halfBorder,
|
|
2722
|
+
y: halfBorder,
|
|
2723
|
+
width: config.frameWidth - borderConfig.width,
|
|
2724
|
+
height: config.frameHeight - borderConfig.width,
|
|
2725
|
+
stroke: {
|
|
2726
|
+
width: borderConfig.width,
|
|
2727
|
+
color: borderConfig.color,
|
|
2728
|
+
opacity: borderConfig.opacity
|
|
2729
|
+
},
|
|
2730
|
+
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
2731
|
+
});
|
|
2755
2732
|
}
|
|
2756
2733
|
const nonActiveOps = [];
|
|
2757
2734
|
const activeOps = [];
|
|
@@ -3098,40 +3075,61 @@ async function createNodePainter(opts) {
|
|
|
3098
3075
|
i++;
|
|
3099
3076
|
}
|
|
3100
3077
|
renderToBoth((context) => {
|
|
3101
|
-
|
|
3102
|
-
|
|
3078
|
+
let bgIdx = 0;
|
|
3079
|
+
while (bgIdx < captionWordOps.length) {
|
|
3080
|
+
const wordOp = captionWordOps[bgIdx];
|
|
3081
|
+
if (!wordOp.background) {
|
|
3082
|
+
bgIdx++;
|
|
3083
|
+
continue;
|
|
3084
|
+
}
|
|
3103
3085
|
const wordDisplayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
3104
|
-
if (wordDisplayText.length === 0)
|
|
3086
|
+
if (wordDisplayText.length === 0) {
|
|
3087
|
+
bgIdx++;
|
|
3088
|
+
continue;
|
|
3089
|
+
}
|
|
3090
|
+
const mergeGroup = [wordOp];
|
|
3091
|
+
let nextIdx = bgIdx + 1;
|
|
3092
|
+
while (nextIdx < captionWordOps.length) {
|
|
3093
|
+
const nextWord = captionWordOps[nextIdx];
|
|
3094
|
+
if (!nextWord.background) break;
|
|
3095
|
+
const nextDisplay = getVisibleText(nextWord.text, nextWord.visibleCharacters, nextWord.isRTL);
|
|
3096
|
+
if (nextDisplay.length === 0) break;
|
|
3097
|
+
if (Math.round(nextWord.y) !== Math.round(wordOp.y)) break;
|
|
3098
|
+
if (nextWord.background.color !== wordOp.background.color) break;
|
|
3099
|
+
mergeGroup.push(nextWord);
|
|
3100
|
+
nextIdx++;
|
|
3101
|
+
}
|
|
3102
|
+
const firstWord = mergeGroup[0];
|
|
3103
|
+
const lastWord = mergeGroup[mergeGroup.length - 1];
|
|
3104
|
+
const firstBg = firstWord.background;
|
|
3105
|
+
const mergedLeft = firstWord.x + firstWord.transform.translateX;
|
|
3106
|
+
const mergedRight = lastWord.x + lastWord.transform.translateX + lastWord.width;
|
|
3107
|
+
const mergedWidth = mergedRight - mergedLeft;
|
|
3105
3108
|
context.save();
|
|
3106
|
-
const bgTx = Math.round(
|
|
3107
|
-
const bgTy = Math.round(
|
|
3109
|
+
const bgTx = Math.round(mergedLeft);
|
|
3110
|
+
const bgTy = Math.round(firstWord.y + firstWord.transform.translateY);
|
|
3108
3111
|
context.translate(bgTx, bgTy);
|
|
3109
|
-
if (
|
|
3110
|
-
const halfWidth =
|
|
3112
|
+
if (firstWord.transform.scale !== 1) {
|
|
3113
|
+
const halfWidth = mergedWidth / 2;
|
|
3111
3114
|
context.translate(halfWidth, 0);
|
|
3112
|
-
context.scale(
|
|
3115
|
+
context.scale(firstWord.transform.scale, firstWord.transform.scale);
|
|
3113
3116
|
context.translate(-halfWidth, 0);
|
|
3114
3117
|
}
|
|
3115
|
-
context.globalAlpha =
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
if (wordOp.letterSpacing) {
|
|
3119
|
-
context.letterSpacing = `${wordOp.letterSpacing}px`;
|
|
3120
|
-
}
|
|
3121
|
-
const bgTextWidth = wordOp.width;
|
|
3122
|
-
const bgAscent = wordOp.fontSize * ASCENT_RATIO;
|
|
3123
|
-
const bgDescent = wordOp.fontSize * DESCENT_RATIO;
|
|
3118
|
+
context.globalAlpha = firstWord.transform.opacity;
|
|
3119
|
+
const bgAscent = firstWord.fontSize * ASCENT_RATIO;
|
|
3120
|
+
const bgDescent = firstWord.fontSize * DESCENT_RATIO;
|
|
3124
3121
|
const bgTextHeight = bgAscent + bgDescent;
|
|
3125
|
-
const bgX = -
|
|
3126
|
-
const bgY = -bgAscent -
|
|
3127
|
-
const bgW =
|
|
3128
|
-
const bgH = bgTextHeight +
|
|
3129
|
-
const bgC = parseHex6(
|
|
3122
|
+
const bgX = -firstBg.padding;
|
|
3123
|
+
const bgY = -bgAscent - firstBg.padding;
|
|
3124
|
+
const bgW = mergedWidth + firstBg.padding * 2;
|
|
3125
|
+
const bgH = bgTextHeight + firstBg.padding * 2;
|
|
3126
|
+
const bgC = parseHex6(firstBg.color, firstBg.opacity);
|
|
3130
3127
|
context.fillStyle = `rgba(${bgC.r},${bgC.g},${bgC.b},${bgC.a})`;
|
|
3131
3128
|
context.beginPath();
|
|
3132
|
-
roundRectPath(context, bgX, bgY, bgW, bgH,
|
|
3129
|
+
roundRectPath(context, bgX, bgY, bgW, bgH, firstBg.borderRadius);
|
|
3133
3130
|
context.fill();
|
|
3134
3131
|
context.restore();
|
|
3132
|
+
bgIdx = nextIdx;
|
|
3135
3133
|
}
|
|
3136
3134
|
for (const wordOp of captionWordOps) {
|
|
3137
3135
|
const displayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
@@ -4916,16 +4914,17 @@ var CaptionLayoutEngine = class {
|
|
|
4916
4914
|
});
|
|
4917
4915
|
const contentHeight = config.frameHeight - config.padding.top - config.padding.bottom;
|
|
4918
4916
|
const contentWidth = config.frameWidth - config.padding.left - config.padding.right;
|
|
4917
|
+
const ASCENT_RATIO2 = 0.8;
|
|
4919
4918
|
const calculateGroupY = (group) => {
|
|
4920
4919
|
const totalHeight = group.lines.length * config.fontSize * config.lineHeight;
|
|
4921
4920
|
switch (config.verticalAlign) {
|
|
4922
4921
|
case "top":
|
|
4923
|
-
return config.padding.top + config.fontSize *
|
|
4922
|
+
return config.padding.top + config.fontSize * ASCENT_RATIO2;
|
|
4924
4923
|
case "bottom":
|
|
4925
|
-
return config.frameHeight - config.padding.bottom - totalHeight
|
|
4924
|
+
return config.frameHeight - config.padding.bottom - totalHeight + config.fontSize * ASCENT_RATIO2;
|
|
4926
4925
|
case "middle":
|
|
4927
4926
|
default:
|
|
4928
|
-
return config.padding.top + (contentHeight - totalHeight) / 2 + config.fontSize;
|
|
4927
|
+
return config.padding.top + (contentHeight - totalHeight) / 2 + config.fontSize * ASCENT_RATIO2;
|
|
4929
4928
|
}
|
|
4930
4929
|
};
|
|
4931
4930
|
const allWordTexts = store.words.slice(0, store.length);
|
package/dist/entry.web.js
CHANGED
|
@@ -34474,10 +34474,11 @@ function calculateTypewriterState(ctx, charCount, speed) {
|
|
|
34474
34474
|
isActive: isWordActive(ctx)
|
|
34475
34475
|
};
|
|
34476
34476
|
}
|
|
34477
|
-
function calculateNoneState(
|
|
34477
|
+
function calculateNoneState(_ctx) {
|
|
34478
34478
|
return {
|
|
34479
34479
|
opacity: 1,
|
|
34480
|
-
isActive:
|
|
34480
|
+
isActive: false,
|
|
34481
|
+
fillProgress: 0
|
|
34481
34482
|
};
|
|
34482
34483
|
}
|
|
34483
34484
|
function calculateWordAnimationState(wordStart, wordEnd, currentTime, config2, activeScale = 1, charCount = 0, fontSize = 48, isRTL = false) {
|
|
@@ -34520,7 +34521,7 @@ function calculateWordAnimationState(wordStart, wordEnd, currentTime, config2, a
|
|
|
34520
34521
|
break;
|
|
34521
34522
|
}
|
|
34522
34523
|
const mergedState = { ...baseState, ...partialState };
|
|
34523
|
-
if (mergedState.isActive && activeScale !== 1 && config2.style !== "pop") {
|
|
34524
|
+
if (mergedState.isActive && activeScale !== 1 && config2.style !== "pop" && config2.style !== "none") {
|
|
34524
34525
|
mergedState.scale = activeScale;
|
|
34525
34526
|
}
|
|
34526
34527
|
return mergedState;
|
|
@@ -34582,7 +34583,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
34582
34583
|
if (!baseStroke && !activeStroke) {
|
|
34583
34584
|
return void 0;
|
|
34584
34585
|
}
|
|
34585
|
-
if (isActive
|
|
34586
|
+
if (isActive) {
|
|
34587
|
+
if (!activeStroke) {
|
|
34588
|
+
return void 0;
|
|
34589
|
+
}
|
|
34586
34590
|
return {
|
|
34587
34591
|
width: activeStroke.width ?? baseStroke?.width ?? 0,
|
|
34588
34592
|
color: activeStroke.color ?? baseStroke?.color ?? "#000000",
|
|
@@ -34598,7 +34602,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
34598
34602
|
}
|
|
34599
34603
|
return void 0;
|
|
34600
34604
|
}
|
|
34601
|
-
function extractShadowConfig(asset) {
|
|
34605
|
+
function extractShadowConfig(asset, isActive) {
|
|
34606
|
+
if (isActive) {
|
|
34607
|
+
return void 0;
|
|
34608
|
+
}
|
|
34602
34609
|
const shadow = asset.shadow;
|
|
34603
34610
|
if (!shadow) {
|
|
34604
34611
|
return void 0;
|
|
@@ -34705,27 +34712,10 @@ function createDrawCaptionWordOp(word, animState, asset, fontConfig) {
|
|
|
34705
34712
|
visibleCharacters: animState.visibleCharacters,
|
|
34706
34713
|
letterSpacing: fontConfig.letterSpacing > 0 ? fontConfig.letterSpacing : void 0,
|
|
34707
34714
|
stroke: extractStrokeConfig(asset, isActive),
|
|
34708
|
-
shadow: extractShadowConfig(asset),
|
|
34715
|
+
shadow: extractShadowConfig(asset, isActive),
|
|
34709
34716
|
background: extractBackgroundConfig(asset, isActive, fontConfig.size)
|
|
34710
34717
|
};
|
|
34711
34718
|
}
|
|
34712
|
-
function calculateGroupBounds(activeGroup, padding, frameWidth, activeScale, fontSize) {
|
|
34713
|
-
let minY = Infinity;
|
|
34714
|
-
let maxY = -Infinity;
|
|
34715
|
-
for (const line of activeGroup.lines) {
|
|
34716
|
-
const lineY = line.y - line.height * ASCENT_RATIO;
|
|
34717
|
-
const lineBottom = line.y + line.height * DESCENT_RATIO;
|
|
34718
|
-
if (lineY < minY) minY = lineY;
|
|
34719
|
-
if (lineBottom > maxY) maxY = lineBottom;
|
|
34720
|
-
}
|
|
34721
|
-
const scaleExpansion = activeScale > 1 ? (activeScale - 1) * fontSize : 0;
|
|
34722
|
-
return {
|
|
34723
|
-
bgX: 0,
|
|
34724
|
-
bgY: minY - padding.top - scaleExpansion,
|
|
34725
|
-
bgWidth: frameWidth,
|
|
34726
|
-
bgHeight: maxY - minY + padding.top + padding.bottom + scaleExpansion * 2
|
|
34727
|
-
};
|
|
34728
|
-
}
|
|
34729
34719
|
function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, config2) {
|
|
34730
34720
|
if (layout.store.length === 0) {
|
|
34731
34721
|
return [];
|
|
@@ -34745,48 +34735,35 @@ function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, co
|
|
|
34745
34735
|
fontConfig.size
|
|
34746
34736
|
);
|
|
34747
34737
|
const ops = [];
|
|
34748
|
-
const
|
|
34749
|
-
|
|
34750
|
-
|
|
34751
|
-
|
|
34752
|
-
|
|
34753
|
-
|
|
34754
|
-
|
|
34755
|
-
|
|
34756
|
-
|
|
34757
|
-
|
|
34758
|
-
|
|
34759
|
-
);
|
|
34760
|
-
|
|
34761
|
-
|
|
34762
|
-
|
|
34763
|
-
|
|
34764
|
-
|
|
34765
|
-
|
|
34766
|
-
|
|
34767
|
-
|
|
34768
|
-
|
|
34769
|
-
|
|
34770
|
-
|
|
34771
|
-
|
|
34772
|
-
|
|
34773
|
-
|
|
34774
|
-
|
|
34775
|
-
|
|
34776
|
-
|
|
34777
|
-
op: "RectangleStroke",
|
|
34778
|
-
x: bgX + halfBorder,
|
|
34779
|
-
y: bgY + halfBorder,
|
|
34780
|
-
width: bgWidth - borderConfig.width,
|
|
34781
|
-
height: bgHeight - borderConfig.width,
|
|
34782
|
-
stroke: {
|
|
34783
|
-
width: borderConfig.width,
|
|
34784
|
-
color: borderConfig.color,
|
|
34785
|
-
opacity: borderConfig.opacity
|
|
34786
|
-
},
|
|
34787
|
-
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
34788
|
-
});
|
|
34789
|
-
}
|
|
34738
|
+
const captionBg = extractCaptionBackground(asset);
|
|
34739
|
+
if (captionBg) {
|
|
34740
|
+
ops.push({
|
|
34741
|
+
op: "DrawCaptionBackground",
|
|
34742
|
+
x: 0,
|
|
34743
|
+
y: 0,
|
|
34744
|
+
width: config2.frameWidth,
|
|
34745
|
+
height: config2.frameHeight,
|
|
34746
|
+
color: captionBg.color,
|
|
34747
|
+
opacity: captionBg.opacity,
|
|
34748
|
+
borderRadius: captionBg.borderRadius
|
|
34749
|
+
});
|
|
34750
|
+
}
|
|
34751
|
+
const borderConfig = extractCaptionBorder(asset);
|
|
34752
|
+
if (borderConfig) {
|
|
34753
|
+
const halfBorder = borderConfig.width / 2;
|
|
34754
|
+
ops.push({
|
|
34755
|
+
op: "RectangleStroke",
|
|
34756
|
+
x: halfBorder,
|
|
34757
|
+
y: halfBorder,
|
|
34758
|
+
width: config2.frameWidth - borderConfig.width,
|
|
34759
|
+
height: config2.frameHeight - borderConfig.width,
|
|
34760
|
+
stroke: {
|
|
34761
|
+
width: borderConfig.width,
|
|
34762
|
+
color: borderConfig.color,
|
|
34763
|
+
opacity: borderConfig.opacity
|
|
34764
|
+
},
|
|
34765
|
+
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
34766
|
+
});
|
|
34790
34767
|
}
|
|
34791
34768
|
const nonActiveOps = [];
|
|
34792
34769
|
const activeOps = [];
|
|
@@ -35066,38 +35043,59 @@ function createWebPainter(canvas) {
|
|
|
35066
35043
|
captionWordOps.push(nextOp);
|
|
35067
35044
|
i++;
|
|
35068
35045
|
}
|
|
35069
|
-
|
|
35070
|
-
|
|
35046
|
+
let bgIdx = 0;
|
|
35047
|
+
while (bgIdx < captionWordOps.length) {
|
|
35048
|
+
const wordOp = captionWordOps[bgIdx];
|
|
35049
|
+
if (!wordOp.background) {
|
|
35050
|
+
bgIdx++;
|
|
35051
|
+
continue;
|
|
35052
|
+
}
|
|
35071
35053
|
const wordDisplayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
35072
|
-
if (wordDisplayText.length === 0)
|
|
35054
|
+
if (wordDisplayText.length === 0) {
|
|
35055
|
+
bgIdx++;
|
|
35056
|
+
continue;
|
|
35057
|
+
}
|
|
35058
|
+
const mergeGroup = [wordOp];
|
|
35059
|
+
let nextIdx = bgIdx + 1;
|
|
35060
|
+
while (nextIdx < captionWordOps.length) {
|
|
35061
|
+
const nextWord = captionWordOps[nextIdx];
|
|
35062
|
+
if (!nextWord.background) break;
|
|
35063
|
+
const nextDisplay = getVisibleText(nextWord.text, nextWord.visibleCharacters, nextWord.isRTL);
|
|
35064
|
+
if (nextDisplay.length === 0) break;
|
|
35065
|
+
if (Math.round(nextWord.y) !== Math.round(wordOp.y)) break;
|
|
35066
|
+
if (nextWord.background.color !== wordOp.background.color) break;
|
|
35067
|
+
mergeGroup.push(nextWord);
|
|
35068
|
+
nextIdx++;
|
|
35069
|
+
}
|
|
35070
|
+
const firstWord = mergeGroup[0];
|
|
35071
|
+
const lastWord = mergeGroup[mergeGroup.length - 1];
|
|
35072
|
+
const firstBg = firstWord.background;
|
|
35073
|
+
const mergedLeft = firstWord.x + firstWord.transform.translateX;
|
|
35074
|
+
const mergedRight = lastWord.x + lastWord.transform.translateX + lastWord.width;
|
|
35075
|
+
const mergedWidth = mergedRight - mergedLeft;
|
|
35073
35076
|
ctx.save();
|
|
35074
|
-
const bgTx = Math.round(
|
|
35075
|
-
const bgTy = Math.round(
|
|
35077
|
+
const bgTx = Math.round(mergedLeft);
|
|
35078
|
+
const bgTy = Math.round(firstWord.y + firstWord.transform.translateY);
|
|
35076
35079
|
ctx.translate(bgTx, bgTy);
|
|
35077
|
-
if (
|
|
35078
|
-
const halfWidth =
|
|
35080
|
+
if (firstWord.transform.scale !== 1) {
|
|
35081
|
+
const halfWidth = mergedWidth / 2;
|
|
35079
35082
|
ctx.translate(halfWidth, 0);
|
|
35080
|
-
ctx.scale(
|
|
35083
|
+
ctx.scale(firstWord.transform.scale, firstWord.transform.scale);
|
|
35081
35084
|
ctx.translate(-halfWidth, 0);
|
|
35082
35085
|
}
|
|
35083
|
-
ctx.globalAlpha =
|
|
35084
|
-
|
|
35085
|
-
|
|
35086
|
-
if (wordOp.letterSpacing) {
|
|
35087
|
-
ctx.letterSpacing = `${wordOp.letterSpacing}px`;
|
|
35088
|
-
}
|
|
35089
|
-
const bgTextWidth = wordOp.width;
|
|
35090
|
-
const bgAscent = wordOp.fontSize * ASCENT_RATIO;
|
|
35091
|
-
const bgDescent = wordOp.fontSize * DESCENT_RATIO;
|
|
35086
|
+
ctx.globalAlpha = firstWord.transform.opacity;
|
|
35087
|
+
const bgAscent = firstWord.fontSize * ASCENT_RATIO;
|
|
35088
|
+
const bgDescent = firstWord.fontSize * DESCENT_RATIO;
|
|
35092
35089
|
const bgTextHeight = bgAscent + bgDescent;
|
|
35093
|
-
const bgX = -
|
|
35094
|
-
const bgY = -bgAscent -
|
|
35095
|
-
const bgW =
|
|
35096
|
-
const bgH = bgTextHeight +
|
|
35097
|
-
const bgC = parseHex6(
|
|
35090
|
+
const bgX = -firstBg.padding;
|
|
35091
|
+
const bgY = -bgAscent - firstBg.padding;
|
|
35092
|
+
const bgW = mergedWidth + firstBg.padding * 2;
|
|
35093
|
+
const bgH = bgTextHeight + firstBg.padding * 2;
|
|
35094
|
+
const bgC = parseHex6(firstBg.color, firstBg.opacity);
|
|
35098
35095
|
ctx.fillStyle = `rgba(${bgC.r},${bgC.g},${bgC.b},${bgC.a})`;
|
|
35099
|
-
drawRoundedRect(ctx, bgX, bgY, bgW, bgH,
|
|
35096
|
+
drawRoundedRect(ctx, bgX, bgY, bgW, bgH, firstBg.borderRadius);
|
|
35100
35097
|
ctx.restore();
|
|
35098
|
+
bgIdx = nextIdx;
|
|
35101
35099
|
}
|
|
35102
35100
|
for (const wordOp of captionWordOps) {
|
|
35103
35101
|
const displayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
@@ -37222,16 +37220,17 @@ var CaptionLayoutEngine = class {
|
|
|
37222
37220
|
});
|
|
37223
37221
|
const contentHeight = config2.frameHeight - config2.padding.top - config2.padding.bottom;
|
|
37224
37222
|
const contentWidth = config2.frameWidth - config2.padding.left - config2.padding.right;
|
|
37223
|
+
const ASCENT_RATIO2 = 0.8;
|
|
37225
37224
|
const calculateGroupY = (group) => {
|
|
37226
37225
|
const totalHeight = group.lines.length * config2.fontSize * config2.lineHeight;
|
|
37227
37226
|
switch (config2.verticalAlign) {
|
|
37228
37227
|
case "top":
|
|
37229
|
-
return config2.padding.top + config2.fontSize *
|
|
37228
|
+
return config2.padding.top + config2.fontSize * ASCENT_RATIO2;
|
|
37230
37229
|
case "bottom":
|
|
37231
|
-
return config2.frameHeight - config2.padding.bottom - totalHeight
|
|
37230
|
+
return config2.frameHeight - config2.padding.bottom - totalHeight + config2.fontSize * ASCENT_RATIO2;
|
|
37232
37231
|
case "middle":
|
|
37233
37232
|
default:
|
|
37234
|
-
return config2.padding.top + (contentHeight - totalHeight) / 2 + config2.fontSize;
|
|
37233
|
+
return config2.padding.top + (contentHeight - totalHeight) / 2 + config2.fontSize * ASCENT_RATIO2;
|
|
37235
37234
|
}
|
|
37236
37235
|
};
|
|
37237
37236
|
const allWordTexts = store.words.slice(0, store.length);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shotstack/shotstack-canvas",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Text layout & animation engine (HarfBuzz) for Node & Web - fully self-contained.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/entry.node.cjs",
|
|
@@ -34,8 +34,11 @@
|
|
|
34
34
|
"test:caption-web": "vite dev examples/caption-tests",
|
|
35
35
|
"test:padding-node": "node examples/captionpaddingtests/node-test.mjs",
|
|
36
36
|
"test:padding-web": "vite dev examples/captionpaddingtests",
|
|
37
|
+
"test:fixes-node": "node examples/caption-fix-tests/node-test.mjs",
|
|
38
|
+
"test:fixes-web": "vite dev examples/caption-fix-tests",
|
|
37
39
|
"prepublishOnly": "node scripts/publish-guard.cjs && pnpm build",
|
|
38
|
-
"test": "node --test tests/build-verify.mjs"
|
|
40
|
+
"test": "node --test tests/build-verify.mjs",
|
|
41
|
+
"test:logic": "node --test tests/caption-logic.mjs"
|
|
39
42
|
},
|
|
40
43
|
"publishConfig": {
|
|
41
44
|
"access": "public",
|