@shotstack/shotstack-canvas 2.1.1 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entry.node.cjs +103 -98
- package/dist/entry.node.js +103 -98
- package/dist/entry.web.js +103 -98
- package/package.json +5 -2
package/dist/entry.node.cjs
CHANGED
|
@@ -1851,8 +1851,8 @@ async function buildDrawOps(p) {
|
|
|
1851
1851
|
const canvasCenterY = p.canvas.height / 2;
|
|
1852
1852
|
const bgX = canvasCenterX - contentWidth / 2;
|
|
1853
1853
|
const bgY = canvasCenterY - contentHeight / 2;
|
|
1854
|
-
const maxRadius = Math.min(contentWidth - borderWidth2, contentHeight - borderWidth2) / 2;
|
|
1855
|
-
const outerRadius = Math.min(borderRadius, maxRadius);
|
|
1854
|
+
const maxRadius = Math.max(0, Math.min(contentWidth - borderWidth2, contentHeight - borderWidth2) / 2);
|
|
1855
|
+
const outerRadius = Math.max(0, Math.min(borderRadius, maxRadius));
|
|
1856
1856
|
const innerRadius = Math.max(0, outerRadius - halfBorder);
|
|
1857
1857
|
if (p.background?.color) {
|
|
1858
1858
|
ops.push({
|
|
@@ -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,67 @@ async function createNodePainter(opts) {
|
|
|
3501
3478
|
i++;
|
|
3502
3479
|
}
|
|
3503
3480
|
renderToBoth((context) => {
|
|
3504
|
-
|
|
3505
|
-
|
|
3481
|
+
const bgSortedOps = [...captionWordOps].sort((a, b) => {
|
|
3482
|
+
const dy = Math.round(a.y) - Math.round(b.y);
|
|
3483
|
+
if (dy !== 0) return dy;
|
|
3484
|
+
return a.x + a.transform.translateX - (b.x + b.transform.translateX);
|
|
3485
|
+
});
|
|
3486
|
+
let bgIdx = 0;
|
|
3487
|
+
while (bgIdx < bgSortedOps.length) {
|
|
3488
|
+
const wordOp = bgSortedOps[bgIdx];
|
|
3489
|
+
if (!wordOp.background) {
|
|
3490
|
+
bgIdx++;
|
|
3491
|
+
continue;
|
|
3492
|
+
}
|
|
3506
3493
|
const wordDisplayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
3507
|
-
if (wordDisplayText.length === 0)
|
|
3494
|
+
if (wordDisplayText.length === 0) {
|
|
3495
|
+
bgIdx++;
|
|
3496
|
+
continue;
|
|
3497
|
+
}
|
|
3498
|
+
const mergeGroup = [wordOp];
|
|
3499
|
+
let nextIdx = bgIdx + 1;
|
|
3500
|
+
while (nextIdx < bgSortedOps.length) {
|
|
3501
|
+
const nextWord = bgSortedOps[nextIdx];
|
|
3502
|
+
if (!nextWord.background) break;
|
|
3503
|
+
const nextDisplay = getVisibleText(nextWord.text, nextWord.visibleCharacters, nextWord.isRTL);
|
|
3504
|
+
if (nextDisplay.length === 0) break;
|
|
3505
|
+
if (Math.round(nextWord.y) !== Math.round(wordOp.y)) break;
|
|
3506
|
+
mergeGroup.push(nextWord);
|
|
3507
|
+
nextIdx++;
|
|
3508
|
+
}
|
|
3509
|
+
const firstWord = mergeGroup[0];
|
|
3510
|
+
const lastWord = mergeGroup[mergeGroup.length - 1];
|
|
3511
|
+
const firstBg = firstWord.background;
|
|
3512
|
+
const mergedLeft = firstWord.x + firstWord.transform.translateX;
|
|
3513
|
+
const mergedRight = lastWord.x + lastWord.transform.translateX + lastWord.width;
|
|
3514
|
+
const mergedWidth = mergedRight - mergedLeft;
|
|
3508
3515
|
context.save();
|
|
3509
|
-
const bgTx = Math.round(
|
|
3510
|
-
const bgTy = Math.round(
|
|
3516
|
+
const bgTx = Math.round(mergedLeft);
|
|
3517
|
+
const bgTy = Math.round(firstWord.y + firstWord.transform.translateY);
|
|
3511
3518
|
context.translate(bgTx, bgTy);
|
|
3512
|
-
if (
|
|
3513
|
-
const halfWidth =
|
|
3519
|
+
if (firstWord.transform.scale !== 1) {
|
|
3520
|
+
const halfWidth = mergedWidth / 2;
|
|
3514
3521
|
context.translate(halfWidth, 0);
|
|
3515
|
-
context.scale(
|
|
3522
|
+
context.scale(firstWord.transform.scale, firstWord.transform.scale);
|
|
3516
3523
|
context.translate(-halfWidth, 0);
|
|
3517
3524
|
}
|
|
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;
|
|
3525
|
+
context.globalAlpha = firstWord.transform.opacity;
|
|
3526
|
+
const bgAscent = firstWord.fontSize * ASCENT_RATIO;
|
|
3527
|
+
const bgDescent = firstWord.fontSize * DESCENT_RATIO;
|
|
3527
3528
|
const bgTextHeight = bgAscent + bgDescent;
|
|
3528
|
-
const bgX = -
|
|
3529
|
-
const bgY = -bgAscent -
|
|
3530
|
-
const bgW =
|
|
3531
|
-
const bgH = bgTextHeight +
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3529
|
+
const bgX = -firstBg.padding;
|
|
3530
|
+
const bgY = -bgAscent - firstBg.padding;
|
|
3531
|
+
const bgW = mergedWidth + firstBg.padding * 2;
|
|
3532
|
+
const bgH = bgTextHeight + firstBg.padding * 2;
|
|
3533
|
+
if (bgW > 0 && bgH > 0) {
|
|
3534
|
+
const bgC = parseHex6(firstBg.color, firstBg.opacity);
|
|
3535
|
+
context.fillStyle = `rgba(${bgC.r},${bgC.g},${bgC.b},${bgC.a})`;
|
|
3536
|
+
context.beginPath();
|
|
3537
|
+
roundRectPath(context, bgX, bgY, bgW, bgH, firstBg.borderRadius);
|
|
3538
|
+
context.fill();
|
|
3539
|
+
}
|
|
3537
3540
|
context.restore();
|
|
3541
|
+
bgIdx = nextIdx;
|
|
3538
3542
|
}
|
|
3539
3543
|
for (const wordOp of captionWordOps) {
|
|
3540
3544
|
const displayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
@@ -3829,8 +3833,8 @@ function computePathBounds2(d) {
|
|
|
3829
3833
|
};
|
|
3830
3834
|
}
|
|
3831
3835
|
function roundRectPath(ctx, x, y, w, h, r) {
|
|
3832
|
-
const maxRadius = Math.min(w, h) / 2;
|
|
3833
|
-
const radius = Math.min(r, maxRadius);
|
|
3836
|
+
const maxRadius = Math.max(0, Math.min(w, h) / 2);
|
|
3837
|
+
const radius = Math.max(0, Math.min(r, maxRadius));
|
|
3834
3838
|
ctx.moveTo(x + radius, y);
|
|
3835
3839
|
ctx.arcTo(x + w, y, x + w, y + h, radius);
|
|
3836
3840
|
ctx.arcTo(x + w, y + h, x, y + h, radius);
|
|
@@ -5319,16 +5323,17 @@ var CaptionLayoutEngine = class {
|
|
|
5319
5323
|
});
|
|
5320
5324
|
const contentHeight = config.frameHeight - config.padding.top - config.padding.bottom;
|
|
5321
5325
|
const contentWidth = config.frameWidth - config.padding.left - config.padding.right;
|
|
5326
|
+
const ASCENT_RATIO2 = 0.8;
|
|
5322
5327
|
const calculateGroupY = (group) => {
|
|
5323
5328
|
const totalHeight = group.lines.length * config.fontSize * config.lineHeight;
|
|
5324
5329
|
switch (config.verticalAlign) {
|
|
5325
5330
|
case "top":
|
|
5326
|
-
return config.padding.top + config.fontSize *
|
|
5331
|
+
return config.padding.top + config.fontSize * ASCENT_RATIO2;
|
|
5327
5332
|
case "bottom":
|
|
5328
|
-
return config.frameHeight - config.padding.bottom - totalHeight
|
|
5333
|
+
return config.frameHeight - config.padding.bottom - totalHeight + config.fontSize * ASCENT_RATIO2;
|
|
5329
5334
|
case "middle":
|
|
5330
5335
|
default:
|
|
5331
|
-
return config.padding.top + (contentHeight - totalHeight) / 2 + config.fontSize;
|
|
5336
|
+
return config.padding.top + (contentHeight - totalHeight) / 2 + config.fontSize * ASCENT_RATIO2;
|
|
5332
5337
|
}
|
|
5333
5338
|
};
|
|
5334
5339
|
const allWordTexts = store.words.slice(0, store.length);
|
package/dist/entry.node.js
CHANGED
|
@@ -1448,8 +1448,8 @@ async function buildDrawOps(p) {
|
|
|
1448
1448
|
const canvasCenterY = p.canvas.height / 2;
|
|
1449
1449
|
const bgX = canvasCenterX - contentWidth / 2;
|
|
1450
1450
|
const bgY = canvasCenterY - contentHeight / 2;
|
|
1451
|
-
const maxRadius = Math.min(contentWidth - borderWidth2, contentHeight - borderWidth2) / 2;
|
|
1452
|
-
const outerRadius = Math.min(borderRadius, maxRadius);
|
|
1451
|
+
const maxRadius = Math.max(0, Math.min(contentWidth - borderWidth2, contentHeight - borderWidth2) / 2);
|
|
1452
|
+
const outerRadius = Math.max(0, Math.min(borderRadius, maxRadius));
|
|
1453
1453
|
const innerRadius = Math.max(0, outerRadius - halfBorder);
|
|
1454
1454
|
if (p.background?.color) {
|
|
1455
1455
|
ops.push({
|
|
@@ -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,67 @@ async function createNodePainter(opts) {
|
|
|
3098
3075
|
i++;
|
|
3099
3076
|
}
|
|
3100
3077
|
renderToBoth((context) => {
|
|
3101
|
-
|
|
3102
|
-
|
|
3078
|
+
const bgSortedOps = [...captionWordOps].sort((a, b) => {
|
|
3079
|
+
const dy = Math.round(a.y) - Math.round(b.y);
|
|
3080
|
+
if (dy !== 0) return dy;
|
|
3081
|
+
return a.x + a.transform.translateX - (b.x + b.transform.translateX);
|
|
3082
|
+
});
|
|
3083
|
+
let bgIdx = 0;
|
|
3084
|
+
while (bgIdx < bgSortedOps.length) {
|
|
3085
|
+
const wordOp = bgSortedOps[bgIdx];
|
|
3086
|
+
if (!wordOp.background) {
|
|
3087
|
+
bgIdx++;
|
|
3088
|
+
continue;
|
|
3089
|
+
}
|
|
3103
3090
|
const wordDisplayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
3104
|
-
if (wordDisplayText.length === 0)
|
|
3091
|
+
if (wordDisplayText.length === 0) {
|
|
3092
|
+
bgIdx++;
|
|
3093
|
+
continue;
|
|
3094
|
+
}
|
|
3095
|
+
const mergeGroup = [wordOp];
|
|
3096
|
+
let nextIdx = bgIdx + 1;
|
|
3097
|
+
while (nextIdx < bgSortedOps.length) {
|
|
3098
|
+
const nextWord = bgSortedOps[nextIdx];
|
|
3099
|
+
if (!nextWord.background) break;
|
|
3100
|
+
const nextDisplay = getVisibleText(nextWord.text, nextWord.visibleCharacters, nextWord.isRTL);
|
|
3101
|
+
if (nextDisplay.length === 0) break;
|
|
3102
|
+
if (Math.round(nextWord.y) !== Math.round(wordOp.y)) break;
|
|
3103
|
+
mergeGroup.push(nextWord);
|
|
3104
|
+
nextIdx++;
|
|
3105
|
+
}
|
|
3106
|
+
const firstWord = mergeGroup[0];
|
|
3107
|
+
const lastWord = mergeGroup[mergeGroup.length - 1];
|
|
3108
|
+
const firstBg = firstWord.background;
|
|
3109
|
+
const mergedLeft = firstWord.x + firstWord.transform.translateX;
|
|
3110
|
+
const mergedRight = lastWord.x + lastWord.transform.translateX + lastWord.width;
|
|
3111
|
+
const mergedWidth = mergedRight - mergedLeft;
|
|
3105
3112
|
context.save();
|
|
3106
|
-
const bgTx = Math.round(
|
|
3107
|
-
const bgTy = Math.round(
|
|
3113
|
+
const bgTx = Math.round(mergedLeft);
|
|
3114
|
+
const bgTy = Math.round(firstWord.y + firstWord.transform.translateY);
|
|
3108
3115
|
context.translate(bgTx, bgTy);
|
|
3109
|
-
if (
|
|
3110
|
-
const halfWidth =
|
|
3116
|
+
if (firstWord.transform.scale !== 1) {
|
|
3117
|
+
const halfWidth = mergedWidth / 2;
|
|
3111
3118
|
context.translate(halfWidth, 0);
|
|
3112
|
-
context.scale(
|
|
3119
|
+
context.scale(firstWord.transform.scale, firstWord.transform.scale);
|
|
3113
3120
|
context.translate(-halfWidth, 0);
|
|
3114
3121
|
}
|
|
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;
|
|
3122
|
+
context.globalAlpha = firstWord.transform.opacity;
|
|
3123
|
+
const bgAscent = firstWord.fontSize * ASCENT_RATIO;
|
|
3124
|
+
const bgDescent = firstWord.fontSize * DESCENT_RATIO;
|
|
3124
3125
|
const bgTextHeight = bgAscent + bgDescent;
|
|
3125
|
-
const bgX = -
|
|
3126
|
-
const bgY = -bgAscent -
|
|
3127
|
-
const bgW =
|
|
3128
|
-
const bgH = bgTextHeight +
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3126
|
+
const bgX = -firstBg.padding;
|
|
3127
|
+
const bgY = -bgAscent - firstBg.padding;
|
|
3128
|
+
const bgW = mergedWidth + firstBg.padding * 2;
|
|
3129
|
+
const bgH = bgTextHeight + firstBg.padding * 2;
|
|
3130
|
+
if (bgW > 0 && bgH > 0) {
|
|
3131
|
+
const bgC = parseHex6(firstBg.color, firstBg.opacity);
|
|
3132
|
+
context.fillStyle = `rgba(${bgC.r},${bgC.g},${bgC.b},${bgC.a})`;
|
|
3133
|
+
context.beginPath();
|
|
3134
|
+
roundRectPath(context, bgX, bgY, bgW, bgH, firstBg.borderRadius);
|
|
3135
|
+
context.fill();
|
|
3136
|
+
}
|
|
3134
3137
|
context.restore();
|
|
3138
|
+
bgIdx = nextIdx;
|
|
3135
3139
|
}
|
|
3136
3140
|
for (const wordOp of captionWordOps) {
|
|
3137
3141
|
const displayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
@@ -3426,8 +3430,8 @@ function computePathBounds2(d) {
|
|
|
3426
3430
|
};
|
|
3427
3431
|
}
|
|
3428
3432
|
function roundRectPath(ctx, x, y, w, h, r) {
|
|
3429
|
-
const maxRadius = Math.min(w, h) / 2;
|
|
3430
|
-
const radius = Math.min(r, maxRadius);
|
|
3433
|
+
const maxRadius = Math.max(0, Math.min(w, h) / 2);
|
|
3434
|
+
const radius = Math.max(0, Math.min(r, maxRadius));
|
|
3431
3435
|
ctx.moveTo(x + radius, y);
|
|
3432
3436
|
ctx.arcTo(x + w, y, x + w, y + h, radius);
|
|
3433
3437
|
ctx.arcTo(x + w, y + h, x, y + h, radius);
|
|
@@ -4916,16 +4920,17 @@ var CaptionLayoutEngine = class {
|
|
|
4916
4920
|
});
|
|
4917
4921
|
const contentHeight = config.frameHeight - config.padding.top - config.padding.bottom;
|
|
4918
4922
|
const contentWidth = config.frameWidth - config.padding.left - config.padding.right;
|
|
4923
|
+
const ASCENT_RATIO2 = 0.8;
|
|
4919
4924
|
const calculateGroupY = (group) => {
|
|
4920
4925
|
const totalHeight = group.lines.length * config.fontSize * config.lineHeight;
|
|
4921
4926
|
switch (config.verticalAlign) {
|
|
4922
4927
|
case "top":
|
|
4923
|
-
return config.padding.top + config.fontSize *
|
|
4928
|
+
return config.padding.top + config.fontSize * ASCENT_RATIO2;
|
|
4924
4929
|
case "bottom":
|
|
4925
|
-
return config.frameHeight - config.padding.bottom - totalHeight
|
|
4930
|
+
return config.frameHeight - config.padding.bottom - totalHeight + config.fontSize * ASCENT_RATIO2;
|
|
4926
4931
|
case "middle":
|
|
4927
4932
|
default:
|
|
4928
|
-
return config.padding.top + (contentHeight - totalHeight) / 2 + config.fontSize;
|
|
4933
|
+
return config.padding.top + (contentHeight - totalHeight) / 2 + config.fontSize * ASCENT_RATIO2;
|
|
4929
4934
|
}
|
|
4930
4935
|
};
|
|
4931
4936
|
const allWordTexts = store.words.slice(0, store.length);
|
package/dist/entry.web.js
CHANGED
|
@@ -33483,8 +33483,8 @@ async function buildDrawOps(p) {
|
|
|
33483
33483
|
const canvasCenterY = p.canvas.height / 2;
|
|
33484
33484
|
const bgX = canvasCenterX - contentWidth / 2;
|
|
33485
33485
|
const bgY = canvasCenterY - contentHeight / 2;
|
|
33486
|
-
const maxRadius = Math.min(contentWidth - borderWidth2, contentHeight - borderWidth2) / 2;
|
|
33487
|
-
const outerRadius = Math.min(borderRadius, maxRadius);
|
|
33486
|
+
const maxRadius = Math.max(0, Math.min(contentWidth - borderWidth2, contentHeight - borderWidth2) / 2);
|
|
33487
|
+
const outerRadius = Math.max(0, Math.min(borderRadius, maxRadius));
|
|
33488
33488
|
const innerRadius = Math.max(0, outerRadius - halfBorder);
|
|
33489
33489
|
if (p.background?.color) {
|
|
33490
33490
|
ops.push({
|
|
@@ -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 = [];
|
|
@@ -34960,8 +34937,8 @@ function createWebPainter(canvas) {
|
|
|
34960
34937
|
const y2 = op.y;
|
|
34961
34938
|
const w = op.width;
|
|
34962
34939
|
const h = op.height;
|
|
34963
|
-
const maxRadius = Math.min(w, h) / 2;
|
|
34964
|
-
const r = Math.min(op.borderRadius, maxRadius);
|
|
34940
|
+
const maxRadius = Math.max(0, Math.min(w, h) / 2);
|
|
34941
|
+
const r = Math.max(0, Math.min(op.borderRadius, maxRadius));
|
|
34965
34942
|
p.moveTo(x2 + r, y2);
|
|
34966
34943
|
p.arcTo(x2 + w, y2, x2 + w, y2 + h, r);
|
|
34967
34944
|
p.arcTo(x2 + w, y2 + h, x2, y2 + h, r);
|
|
@@ -35066,38 +35043,65 @@ function createWebPainter(canvas) {
|
|
|
35066
35043
|
captionWordOps.push(nextOp);
|
|
35067
35044
|
i++;
|
|
35068
35045
|
}
|
|
35069
|
-
|
|
35070
|
-
|
|
35046
|
+
const bgSortedOps = [...captionWordOps].sort((a, b) => {
|
|
35047
|
+
const dy = Math.round(a.y) - Math.round(b.y);
|
|
35048
|
+
if (dy !== 0) return dy;
|
|
35049
|
+
return a.x + a.transform.translateX - (b.x + b.transform.translateX);
|
|
35050
|
+
});
|
|
35051
|
+
let bgIdx = 0;
|
|
35052
|
+
while (bgIdx < bgSortedOps.length) {
|
|
35053
|
+
const wordOp = bgSortedOps[bgIdx];
|
|
35054
|
+
if (!wordOp.background) {
|
|
35055
|
+
bgIdx++;
|
|
35056
|
+
continue;
|
|
35057
|
+
}
|
|
35071
35058
|
const wordDisplayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
35072
|
-
if (wordDisplayText.length === 0)
|
|
35059
|
+
if (wordDisplayText.length === 0) {
|
|
35060
|
+
bgIdx++;
|
|
35061
|
+
continue;
|
|
35062
|
+
}
|
|
35063
|
+
const mergeGroup = [wordOp];
|
|
35064
|
+
let nextIdx = bgIdx + 1;
|
|
35065
|
+
while (nextIdx < bgSortedOps.length) {
|
|
35066
|
+
const nextWord = bgSortedOps[nextIdx];
|
|
35067
|
+
if (!nextWord.background) break;
|
|
35068
|
+
const nextDisplay = getVisibleText(nextWord.text, nextWord.visibleCharacters, nextWord.isRTL);
|
|
35069
|
+
if (nextDisplay.length === 0) break;
|
|
35070
|
+
if (Math.round(nextWord.y) !== Math.round(wordOp.y)) break;
|
|
35071
|
+
mergeGroup.push(nextWord);
|
|
35072
|
+
nextIdx++;
|
|
35073
|
+
}
|
|
35074
|
+
const firstWord = mergeGroup[0];
|
|
35075
|
+
const lastWord = mergeGroup[mergeGroup.length - 1];
|
|
35076
|
+
const firstBg = firstWord.background;
|
|
35077
|
+
const mergedLeft = firstWord.x + firstWord.transform.translateX;
|
|
35078
|
+
const mergedRight = lastWord.x + lastWord.transform.translateX + lastWord.width;
|
|
35079
|
+
const mergedWidth = mergedRight - mergedLeft;
|
|
35073
35080
|
ctx.save();
|
|
35074
|
-
const bgTx = Math.round(
|
|
35075
|
-
const bgTy = Math.round(
|
|
35081
|
+
const bgTx = Math.round(mergedLeft);
|
|
35082
|
+
const bgTy = Math.round(firstWord.y + firstWord.transform.translateY);
|
|
35076
35083
|
ctx.translate(bgTx, bgTy);
|
|
35077
|
-
if (
|
|
35078
|
-
const halfWidth =
|
|
35084
|
+
if (firstWord.transform.scale !== 1) {
|
|
35085
|
+
const halfWidth = mergedWidth / 2;
|
|
35079
35086
|
ctx.translate(halfWidth, 0);
|
|
35080
|
-
ctx.scale(
|
|
35087
|
+
ctx.scale(firstWord.transform.scale, firstWord.transform.scale);
|
|
35081
35088
|
ctx.translate(-halfWidth, 0);
|
|
35082
35089
|
}
|
|
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;
|
|
35090
|
+
ctx.globalAlpha = firstWord.transform.opacity;
|
|
35091
|
+
const bgAscent = firstWord.fontSize * ASCENT_RATIO;
|
|
35092
|
+
const bgDescent = firstWord.fontSize * DESCENT_RATIO;
|
|
35092
35093
|
const bgTextHeight = bgAscent + bgDescent;
|
|
35093
|
-
const bgX = -
|
|
35094
|
-
const bgY = -bgAscent -
|
|
35095
|
-
const bgW =
|
|
35096
|
-
const bgH = bgTextHeight +
|
|
35097
|
-
|
|
35098
|
-
|
|
35099
|
-
|
|
35094
|
+
const bgX = -firstBg.padding;
|
|
35095
|
+
const bgY = -bgAscent - firstBg.padding;
|
|
35096
|
+
const bgW = mergedWidth + firstBg.padding * 2;
|
|
35097
|
+
const bgH = bgTextHeight + firstBg.padding * 2;
|
|
35098
|
+
if (bgW > 0 && bgH > 0) {
|
|
35099
|
+
const bgC = parseHex6(firstBg.color, firstBg.opacity);
|
|
35100
|
+
ctx.fillStyle = `rgba(${bgC.r},${bgC.g},${bgC.b},${bgC.a})`;
|
|
35101
|
+
drawRoundedRect(ctx, bgX, bgY, bgW, bgH, firstBg.borderRadius);
|
|
35102
|
+
}
|
|
35100
35103
|
ctx.restore();
|
|
35104
|
+
bgIdx = nextIdx;
|
|
35101
35105
|
}
|
|
35102
35106
|
for (const wordOp of captionWordOps) {
|
|
35103
35107
|
const displayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
@@ -35197,8 +35201,8 @@ function createWebPainter(canvas) {
|
|
|
35197
35201
|
};
|
|
35198
35202
|
}
|
|
35199
35203
|
function drawRoundedRect(ctx, x2, y2, w, h, r) {
|
|
35200
|
-
const maxRadius = Math.min(w, h) / 2;
|
|
35201
|
-
const radius = Math.min(r, maxRadius);
|
|
35204
|
+
const maxRadius = Math.max(0, Math.min(w, h) / 2);
|
|
35205
|
+
const radius = Math.max(0, Math.min(r, maxRadius));
|
|
35202
35206
|
const p = new Path2D();
|
|
35203
35207
|
p.moveTo(x2 + radius, y2);
|
|
35204
35208
|
p.arcTo(x2 + w, y2, x2 + w, y2 + h, radius);
|
|
@@ -37222,16 +37226,17 @@ var CaptionLayoutEngine = class {
|
|
|
37222
37226
|
});
|
|
37223
37227
|
const contentHeight = config2.frameHeight - config2.padding.top - config2.padding.bottom;
|
|
37224
37228
|
const contentWidth = config2.frameWidth - config2.padding.left - config2.padding.right;
|
|
37229
|
+
const ASCENT_RATIO2 = 0.8;
|
|
37225
37230
|
const calculateGroupY = (group) => {
|
|
37226
37231
|
const totalHeight = group.lines.length * config2.fontSize * config2.lineHeight;
|
|
37227
37232
|
switch (config2.verticalAlign) {
|
|
37228
37233
|
case "top":
|
|
37229
|
-
return config2.padding.top + config2.fontSize *
|
|
37234
|
+
return config2.padding.top + config2.fontSize * ASCENT_RATIO2;
|
|
37230
37235
|
case "bottom":
|
|
37231
|
-
return config2.frameHeight - config2.padding.bottom - totalHeight
|
|
37236
|
+
return config2.frameHeight - config2.padding.bottom - totalHeight + config2.fontSize * ASCENT_RATIO2;
|
|
37232
37237
|
case "middle":
|
|
37233
37238
|
default:
|
|
37234
|
-
return config2.padding.top + (contentHeight - totalHeight) / 2 + config2.fontSize;
|
|
37239
|
+
return config2.padding.top + (contentHeight - totalHeight) / 2 + config2.fontSize * ASCENT_RATIO2;
|
|
37235
37240
|
}
|
|
37236
37241
|
};
|
|
37237
37242
|
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.3",
|
|
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",
|