@shotstack/shotstack-canvas 1.5.5 → 1.5.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 +72 -42
- package/dist/entry.node.d.cts +7 -8
- package/dist/entry.node.d.ts +7 -8
- package/dist/entry.node.js +72 -42
- package/dist/entry.web.d.ts +7 -8
- package/dist/entry.web.js +71 -41
- package/package.json +1 -1
package/dist/entry.node.cjs
CHANGED
|
@@ -100,7 +100,8 @@ var fontSchema = import_joi.default.object({
|
|
|
100
100
|
size: import_joi.default.number().min(CANVAS_CONFIG.LIMITS.minFontSize).max(CANVAS_CONFIG.LIMITS.maxFontSize).default(CANVAS_CONFIG.DEFAULTS.fontSize),
|
|
101
101
|
weight: import_joi.default.alternatives().try(import_joi.default.string(), import_joi.default.number()).default("400"),
|
|
102
102
|
color: import_joi.default.string().pattern(HEX6).default(CANVAS_CONFIG.DEFAULTS.color),
|
|
103
|
-
opacity: import_joi.default.number().min(0).max(1).default(1)
|
|
103
|
+
opacity: import_joi.default.number().min(0).max(1).default(1),
|
|
104
|
+
background: import_joi.default.string().pattern(HEX6).optional()
|
|
104
105
|
}).unknown(false);
|
|
105
106
|
var styleSchema = import_joi.default.object({
|
|
106
107
|
letterSpacing: import_joi.default.number().default(0),
|
|
@@ -136,15 +137,11 @@ var borderSchema = import_joi.default.object({
|
|
|
136
137
|
width: import_joi.default.number().min(0).default(0),
|
|
137
138
|
color: import_joi.default.string().pattern(HEX6).default("#000000"),
|
|
138
139
|
opacity: import_joi.default.number().min(0).max(1).default(1),
|
|
139
|
-
|
|
140
|
+
radius: import_joi.default.number().min(0).default(0)
|
|
140
141
|
}).unknown(false);
|
|
141
142
|
var backgroundSchema = import_joi.default.object({
|
|
142
143
|
color: import_joi.default.string().pattern(HEX6).optional(),
|
|
143
|
-
opacity: import_joi.default.number().min(0).max(1).default(1)
|
|
144
|
-
backgroundRadius: import_joi.default.number().min(0).default(0),
|
|
145
|
-
borderRadius: import_joi.default.number().min(0).optional(),
|
|
146
|
-
// Deprecated: use backgroundRadius
|
|
147
|
-
border: borderSchema.optional()
|
|
144
|
+
opacity: import_joi.default.number().min(0).max(1).default(1)
|
|
148
145
|
}).unknown(false);
|
|
149
146
|
var paddingSchema = import_joi.default.alternatives().try(
|
|
150
147
|
import_joi.default.number().min(0).default(0),
|
|
@@ -172,6 +169,7 @@ var RichTextAssetSchema = import_joi.default.object({
|
|
|
172
169
|
stroke: strokeSchema.optional(),
|
|
173
170
|
shadow: shadowSchema.optional(),
|
|
174
171
|
background: backgroundSchema.optional(),
|
|
172
|
+
border: borderSchema.optional(),
|
|
175
173
|
padding: paddingSchema.optional(),
|
|
176
174
|
align: alignmentSchema.optional(),
|
|
177
175
|
animation: animationSchema.optional(),
|
|
@@ -857,7 +855,7 @@ function normalizePadding(padding) {
|
|
|
857
855
|
async function buildDrawOps(p) {
|
|
858
856
|
const ops = [];
|
|
859
857
|
const padding = normalizePadding(p.padding);
|
|
860
|
-
const borderWidth = p.
|
|
858
|
+
const borderWidth = p.border?.width ?? 0;
|
|
861
859
|
ops.push({
|
|
862
860
|
op: "BeginFrame",
|
|
863
861
|
width: p.canvas.width,
|
|
@@ -890,6 +888,7 @@ async function buildDrawOps(p) {
|
|
|
890
888
|
const fill = p.style.gradient ? gradientSpecFrom(p.style.gradient, 1) : { kind: "solid", color: p.font.color, opacity: p.font.opacity };
|
|
891
889
|
const decoColor = p.style.gradient ? p.style.gradient.stops[p.style.gradient.stops.length - 1]?.color ?? p.font.color : p.font.color;
|
|
892
890
|
const textOps = [];
|
|
891
|
+
const highlighterOps = [];
|
|
893
892
|
let gMinX = Infinity, gMinY = Infinity, gMaxX = -Infinity, gMaxY = -Infinity;
|
|
894
893
|
for (const line of p.lines) {
|
|
895
894
|
let lineX;
|
|
@@ -908,6 +907,20 @@ async function buildDrawOps(p) {
|
|
|
908
907
|
let xCursor = lineX;
|
|
909
908
|
const lineIndex = p.lines.indexOf(line);
|
|
910
909
|
const baselineY = blockY + lineIndex * lineHeightPx;
|
|
910
|
+
if (p.font.background) {
|
|
911
|
+
const highlightPadding = p.font.size * 0.15;
|
|
912
|
+
const verticalPadding = p.font.size * 0.12;
|
|
913
|
+
const highlightHeight = p.font.size * 0.92 + verticalPadding * 2;
|
|
914
|
+
const highlightY = baselineY - p.font.size * 0.78 - verticalPadding;
|
|
915
|
+
highlighterOps.push({
|
|
916
|
+
op: "Rectangle",
|
|
917
|
+
x: lineX - highlightPadding,
|
|
918
|
+
y: highlightY,
|
|
919
|
+
width: line.width + highlightPadding * 2,
|
|
920
|
+
height: highlightHeight,
|
|
921
|
+
fill: { kind: "solid", color: p.font.background, opacity: 1 }
|
|
922
|
+
});
|
|
923
|
+
}
|
|
911
924
|
for (const glyph of line.glyphs) {
|
|
912
925
|
const path = await p.glyphPathProvider(glyph.id, glyph.fontDesc);
|
|
913
926
|
if (!path || path === "M 0 0") {
|
|
@@ -983,43 +996,45 @@ async function buildDrawOps(p) {
|
|
|
983
996
|
}
|
|
984
997
|
}
|
|
985
998
|
}
|
|
986
|
-
if (p.background
|
|
999
|
+
if (p.background || p.border) {
|
|
987
1000
|
const bgX = 0;
|
|
988
1001
|
const bgY = 0;
|
|
989
1002
|
const bgWidth = p.canvas.width;
|
|
990
1003
|
const bgHeight = p.canvas.height;
|
|
991
|
-
const
|
|
992
|
-
const
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1004
|
+
const borderWidth2 = p.border?.width ?? 0;
|
|
1005
|
+
const borderRadius = p.border?.radius ?? 0;
|
|
1006
|
+
const halfBorder = borderWidth2 / 2;
|
|
1007
|
+
const maxRadius = Math.min(bgWidth - borderWidth2, bgHeight - borderWidth2) / 2;
|
|
1008
|
+
const outerRadius = Math.min(borderRadius, maxRadius);
|
|
1009
|
+
const innerRadius = Math.max(0, outerRadius - halfBorder);
|
|
1010
|
+
if (p.background?.color) {
|
|
996
1011
|
ops.push({
|
|
997
1012
|
op: "Rectangle",
|
|
998
|
-
x: bgX +
|
|
999
|
-
y: bgY +
|
|
1000
|
-
width: bgWidth -
|
|
1001
|
-
height: bgHeight -
|
|
1013
|
+
x: bgX + borderWidth2,
|
|
1014
|
+
y: bgY + borderWidth2,
|
|
1015
|
+
width: bgWidth - borderWidth2 * 2,
|
|
1016
|
+
height: bgHeight - borderWidth2 * 2,
|
|
1002
1017
|
fill: { kind: "solid", color: p.background.color, opacity: p.background.opacity },
|
|
1003
|
-
borderRadius:
|
|
1018
|
+
borderRadius: innerRadius
|
|
1004
1019
|
});
|
|
1005
1020
|
}
|
|
1006
|
-
if (p.
|
|
1007
|
-
const borderRadius = p.background.border.borderRadius !== void 0 ? p.background.border.borderRadius : backgroundRadius;
|
|
1021
|
+
if (p.border && p.border.width > 0) {
|
|
1008
1022
|
ops.push({
|
|
1009
1023
|
op: "RectangleStroke",
|
|
1010
|
-
x: bgX,
|
|
1011
|
-
y: bgY,
|
|
1012
|
-
width: bgWidth,
|
|
1013
|
-
height: bgHeight,
|
|
1024
|
+
x: bgX + halfBorder,
|
|
1025
|
+
y: bgY + halfBorder,
|
|
1026
|
+
width: bgWidth - borderWidth2,
|
|
1027
|
+
height: bgHeight - borderWidth2,
|
|
1014
1028
|
stroke: {
|
|
1015
|
-
width: p.
|
|
1016
|
-
color: p.
|
|
1017
|
-
opacity: p.
|
|
1029
|
+
width: p.border.width,
|
|
1030
|
+
color: p.border.color,
|
|
1031
|
+
opacity: p.border.opacity
|
|
1018
1032
|
},
|
|
1019
|
-
borderRadius
|
|
1033
|
+
borderRadius: outerRadius
|
|
1020
1034
|
});
|
|
1021
1035
|
}
|
|
1022
1036
|
}
|
|
1037
|
+
ops.push(...highlighterOps);
|
|
1023
1038
|
ops.push(...textOps);
|
|
1024
1039
|
return ops;
|
|
1025
1040
|
}
|
|
@@ -1147,7 +1162,9 @@ function applyTypewriterAnimation(ops, lines, progress, style, fontSize, time, d
|
|
|
1147
1162
|
const wordSegments = getWordSegments(lines);
|
|
1148
1163
|
const totalWords = wordSegments.length;
|
|
1149
1164
|
const visibleWords = Math.floor(progress * totalWords);
|
|
1150
|
-
if (visibleWords === 0)
|
|
1165
|
+
if (visibleWords === 0) {
|
|
1166
|
+
return ops.filter((x) => x.op === "BeginFrame" || x.op === "Rectangle" || x.op === "RectangleStroke");
|
|
1167
|
+
}
|
|
1151
1168
|
let totalVisibleGlyphs = 0;
|
|
1152
1169
|
for (let i = 0; i < Math.min(visibleWords, wordSegments.length); i++) {
|
|
1153
1170
|
totalVisibleGlyphs += wordSegments[i].glyphCount;
|
|
@@ -1161,7 +1178,9 @@ function applyTypewriterAnimation(ops, lines, progress, style, fontSize, time, d
|
|
|
1161
1178
|
} else {
|
|
1162
1179
|
const totalGlyphs = lines.reduce((s, l) => s + l.glyphs.length, 0);
|
|
1163
1180
|
const visibleGlyphs = Math.floor(progress * totalGlyphs);
|
|
1164
|
-
if (visibleGlyphs === 0)
|
|
1181
|
+
if (visibleGlyphs === 0) {
|
|
1182
|
+
return ops.filter((x) => x.op === "BeginFrame" || x.op === "Rectangle" || x.op === "RectangleStroke");
|
|
1183
|
+
}
|
|
1165
1184
|
const visibleOpsRaw = sliceGlyphOps(ops, visibleGlyphs);
|
|
1166
1185
|
const visibleOps = progress >= DECORATION_DONE_THRESHOLD ? visibleOpsRaw : visibleOpsRaw.filter((o) => o.op !== "DecorationLine");
|
|
1167
1186
|
if (progress < 1 && visibleGlyphs > 0) {
|
|
@@ -1177,8 +1196,10 @@ function applyAscendAnimation(ops, lines, progress, direction, fontSize, duratio
|
|
|
1177
1196
|
const result = [];
|
|
1178
1197
|
let glyphIndex = 0;
|
|
1179
1198
|
for (const op of ops) {
|
|
1180
|
-
if (op.op === "BeginFrame") {
|
|
1199
|
+
if (op.op === "BeginFrame" || op.op === "Rectangle" || op.op === "RectangleStroke") {
|
|
1181
1200
|
result.push(op);
|
|
1201
|
+
}
|
|
1202
|
+
if (op.op === "FillPath" || op.op === "StrokePath") {
|
|
1182
1203
|
break;
|
|
1183
1204
|
}
|
|
1184
1205
|
}
|
|
@@ -1237,8 +1258,10 @@ function applyShiftAnimation(ops, lines, progress, direction, fontSize, style, d
|
|
|
1237
1258
|
if (totalUnits === 0) return ops;
|
|
1238
1259
|
const result = [];
|
|
1239
1260
|
for (const op of ops) {
|
|
1240
|
-
if (op.op === "BeginFrame") {
|
|
1261
|
+
if (op.op === "BeginFrame" || op.op === "Rectangle" || op.op === "RectangleStroke") {
|
|
1241
1262
|
result.push(op);
|
|
1263
|
+
}
|
|
1264
|
+
if (op.op === "FillPath" || op.op === "StrokePath") {
|
|
1242
1265
|
break;
|
|
1243
1266
|
}
|
|
1244
1267
|
}
|
|
@@ -1512,7 +1535,7 @@ function sliceGlyphOps(ops, maxGlyphs) {
|
|
|
1512
1535
|
let glyphCount = 0;
|
|
1513
1536
|
let foundGlyphs = false;
|
|
1514
1537
|
for (const op of ops) {
|
|
1515
|
-
if (op.op === "BeginFrame") {
|
|
1538
|
+
if (op.op === "BeginFrame" || op.op === "Rectangle" || op.op === "RectangleStroke") {
|
|
1516
1539
|
result.push(op);
|
|
1517
1540
|
continue;
|
|
1518
1541
|
}
|
|
@@ -1540,9 +1563,11 @@ function sliceGlyphOps(ops, maxGlyphs) {
|
|
|
1540
1563
|
return result;
|
|
1541
1564
|
}
|
|
1542
1565
|
function addTypewriterCursor(ops, glyphCount, fontSize, time) {
|
|
1543
|
-
|
|
1566
|
+
if (glyphCount === 0) return ops;
|
|
1567
|
+
const blinkRate = 1;
|
|
1544
1568
|
const cursorVisible = Math.floor(time * blinkRate * 2) % 2 === 0;
|
|
1545
|
-
|
|
1569
|
+
const alwaysShowCursor = true;
|
|
1570
|
+
if (!alwaysShowCursor && !cursorVisible) return ops;
|
|
1546
1571
|
let last = null;
|
|
1547
1572
|
let count = 0;
|
|
1548
1573
|
for (const op of ops) {
|
|
@@ -1558,13 +1583,16 @@ function addTypewriterCursor(ops, glyphCount, fontSize, time) {
|
|
|
1558
1583
|
const color = getTextColorFromOps(ops);
|
|
1559
1584
|
const cursorX = last.x + fontSize * 0.5;
|
|
1560
1585
|
const cursorY = last.y;
|
|
1586
|
+
const cursorWidth = Math.max(3, fontSize / 15);
|
|
1561
1587
|
const cursorOp = {
|
|
1562
1588
|
op: "DecorationLine",
|
|
1563
|
-
from: { x: cursorX, y: cursorY - fontSize * 0.
|
|
1564
|
-
|
|
1565
|
-
|
|
1589
|
+
from: { x: cursorX, y: cursorY - fontSize * 0.75 },
|
|
1590
|
+
// Slightly taller
|
|
1591
|
+
to: { x: cursorX, y: cursorY + fontSize * 0.15 },
|
|
1592
|
+
width: cursorWidth,
|
|
1566
1593
|
color,
|
|
1567
|
-
opacity: 1
|
|
1594
|
+
opacity: alwaysShowCursor ? 1 : cursorVisible ? 1 : 0
|
|
1595
|
+
// Always visible or blink
|
|
1568
1596
|
};
|
|
1569
1597
|
return [...ops, cursorOp];
|
|
1570
1598
|
}
|
|
@@ -2454,7 +2482,8 @@ async function createTextEngine(opts = {}) {
|
|
|
2454
2482
|
size: main.size,
|
|
2455
2483
|
weight: `${main.weight}`,
|
|
2456
2484
|
color: main.color,
|
|
2457
|
-
opacity: main.opacity
|
|
2485
|
+
opacity: main.opacity,
|
|
2486
|
+
background: main.background
|
|
2458
2487
|
},
|
|
2459
2488
|
style: {
|
|
2460
2489
|
lineHeight: asset.style?.lineHeight ?? 1.2,
|
|
@@ -2468,6 +2497,7 @@ async function createTextEngine(opts = {}) {
|
|
|
2468
2497
|
vertical: asset.align?.vertical ?? "middle"
|
|
2469
2498
|
},
|
|
2470
2499
|
background: asset.background,
|
|
2500
|
+
border: asset.border,
|
|
2471
2501
|
padding: asset.padding,
|
|
2472
2502
|
glyphPathProvider: (gid, fontDesc) => fonts.glyphPath(fontDesc || desc, gid),
|
|
2473
2503
|
getUnitsPerEm: (fontDesc) => fonts.getUnitsPerEm(fontDesc || desc)
|
|
@@ -2520,7 +2550,7 @@ async function createTextEngine(opts = {}) {
|
|
|
2520
2550
|
try {
|
|
2521
2551
|
const hasBackground = !!asset.background?.color;
|
|
2522
2552
|
const hasAnimation = !!asset.animation?.preset;
|
|
2523
|
-
const hasBorderRadius = (asset.
|
|
2553
|
+
const hasBorderRadius = (asset.border?.radius ?? 0) > 0;
|
|
2524
2554
|
const needsAlpha = !hasBackground && hasAnimation || hasBorderRadius;
|
|
2525
2555
|
console.log(
|
|
2526
2556
|
`\u{1F3A8} Video settings: Animation=${hasAnimation}, Background=${hasBackground}, BorderRadius=${hasBorderRadius}, Alpha=${needsAlpha}`
|
package/dist/entry.node.d.cts
CHANGED
|
@@ -33,6 +33,7 @@ type RichTextValidated = Required<{
|
|
|
33
33
|
weight: string | number;
|
|
34
34
|
color: string;
|
|
35
35
|
opacity: number;
|
|
36
|
+
background?: string;
|
|
36
37
|
};
|
|
37
38
|
style?: {
|
|
38
39
|
letterSpacing: number;
|
|
@@ -63,14 +64,12 @@ type RichTextValidated = Required<{
|
|
|
63
64
|
background?: {
|
|
64
65
|
color?: string;
|
|
65
66
|
opacity: number;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
borderRadius?: number;
|
|
73
|
-
};
|
|
67
|
+
};
|
|
68
|
+
border?: {
|
|
69
|
+
width: number;
|
|
70
|
+
color: string;
|
|
71
|
+
opacity: number;
|
|
72
|
+
radius: number;
|
|
74
73
|
};
|
|
75
74
|
padding?: number | {
|
|
76
75
|
top: number;
|
package/dist/entry.node.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ type RichTextValidated = Required<{
|
|
|
33
33
|
weight: string | number;
|
|
34
34
|
color: string;
|
|
35
35
|
opacity: number;
|
|
36
|
+
background?: string;
|
|
36
37
|
};
|
|
37
38
|
style?: {
|
|
38
39
|
letterSpacing: number;
|
|
@@ -63,14 +64,12 @@ type RichTextValidated = Required<{
|
|
|
63
64
|
background?: {
|
|
64
65
|
color?: string;
|
|
65
66
|
opacity: number;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
borderRadius?: number;
|
|
73
|
-
};
|
|
67
|
+
};
|
|
68
|
+
border?: {
|
|
69
|
+
width: number;
|
|
70
|
+
color: string;
|
|
71
|
+
opacity: number;
|
|
72
|
+
radius: number;
|
|
74
73
|
};
|
|
75
74
|
padding?: number | {
|
|
76
75
|
top: number;
|
package/dist/entry.node.js
CHANGED
|
@@ -62,7 +62,8 @@ var fontSchema = Joi.object({
|
|
|
62
62
|
size: Joi.number().min(CANVAS_CONFIG.LIMITS.minFontSize).max(CANVAS_CONFIG.LIMITS.maxFontSize).default(CANVAS_CONFIG.DEFAULTS.fontSize),
|
|
63
63
|
weight: Joi.alternatives().try(Joi.string(), Joi.number()).default("400"),
|
|
64
64
|
color: Joi.string().pattern(HEX6).default(CANVAS_CONFIG.DEFAULTS.color),
|
|
65
|
-
opacity: Joi.number().min(0).max(1).default(1)
|
|
65
|
+
opacity: Joi.number().min(0).max(1).default(1),
|
|
66
|
+
background: Joi.string().pattern(HEX6).optional()
|
|
66
67
|
}).unknown(false);
|
|
67
68
|
var styleSchema = Joi.object({
|
|
68
69
|
letterSpacing: Joi.number().default(0),
|
|
@@ -98,15 +99,11 @@ var borderSchema = Joi.object({
|
|
|
98
99
|
width: Joi.number().min(0).default(0),
|
|
99
100
|
color: Joi.string().pattern(HEX6).default("#000000"),
|
|
100
101
|
opacity: Joi.number().min(0).max(1).default(1),
|
|
101
|
-
|
|
102
|
+
radius: Joi.number().min(0).default(0)
|
|
102
103
|
}).unknown(false);
|
|
103
104
|
var backgroundSchema = Joi.object({
|
|
104
105
|
color: Joi.string().pattern(HEX6).optional(),
|
|
105
|
-
opacity: Joi.number().min(0).max(1).default(1)
|
|
106
|
-
backgroundRadius: Joi.number().min(0).default(0),
|
|
107
|
-
borderRadius: Joi.number().min(0).optional(),
|
|
108
|
-
// Deprecated: use backgroundRadius
|
|
109
|
-
border: borderSchema.optional()
|
|
106
|
+
opacity: Joi.number().min(0).max(1).default(1)
|
|
110
107
|
}).unknown(false);
|
|
111
108
|
var paddingSchema = Joi.alternatives().try(
|
|
112
109
|
Joi.number().min(0).default(0),
|
|
@@ -134,6 +131,7 @@ var RichTextAssetSchema = Joi.object({
|
|
|
134
131
|
stroke: strokeSchema.optional(),
|
|
135
132
|
shadow: shadowSchema.optional(),
|
|
136
133
|
background: backgroundSchema.optional(),
|
|
134
|
+
border: borderSchema.optional(),
|
|
137
135
|
padding: paddingSchema.optional(),
|
|
138
136
|
align: alignmentSchema.optional(),
|
|
139
137
|
animation: animationSchema.optional(),
|
|
@@ -818,7 +816,7 @@ function normalizePadding(padding) {
|
|
|
818
816
|
async function buildDrawOps(p) {
|
|
819
817
|
const ops = [];
|
|
820
818
|
const padding = normalizePadding(p.padding);
|
|
821
|
-
const borderWidth = p.
|
|
819
|
+
const borderWidth = p.border?.width ?? 0;
|
|
822
820
|
ops.push({
|
|
823
821
|
op: "BeginFrame",
|
|
824
822
|
width: p.canvas.width,
|
|
@@ -851,6 +849,7 @@ async function buildDrawOps(p) {
|
|
|
851
849
|
const fill = p.style.gradient ? gradientSpecFrom(p.style.gradient, 1) : { kind: "solid", color: p.font.color, opacity: p.font.opacity };
|
|
852
850
|
const decoColor = p.style.gradient ? p.style.gradient.stops[p.style.gradient.stops.length - 1]?.color ?? p.font.color : p.font.color;
|
|
853
851
|
const textOps = [];
|
|
852
|
+
const highlighterOps = [];
|
|
854
853
|
let gMinX = Infinity, gMinY = Infinity, gMaxX = -Infinity, gMaxY = -Infinity;
|
|
855
854
|
for (const line of p.lines) {
|
|
856
855
|
let lineX;
|
|
@@ -869,6 +868,20 @@ async function buildDrawOps(p) {
|
|
|
869
868
|
let xCursor = lineX;
|
|
870
869
|
const lineIndex = p.lines.indexOf(line);
|
|
871
870
|
const baselineY = blockY + lineIndex * lineHeightPx;
|
|
871
|
+
if (p.font.background) {
|
|
872
|
+
const highlightPadding = p.font.size * 0.15;
|
|
873
|
+
const verticalPadding = p.font.size * 0.12;
|
|
874
|
+
const highlightHeight = p.font.size * 0.92 + verticalPadding * 2;
|
|
875
|
+
const highlightY = baselineY - p.font.size * 0.78 - verticalPadding;
|
|
876
|
+
highlighterOps.push({
|
|
877
|
+
op: "Rectangle",
|
|
878
|
+
x: lineX - highlightPadding,
|
|
879
|
+
y: highlightY,
|
|
880
|
+
width: line.width + highlightPadding * 2,
|
|
881
|
+
height: highlightHeight,
|
|
882
|
+
fill: { kind: "solid", color: p.font.background, opacity: 1 }
|
|
883
|
+
});
|
|
884
|
+
}
|
|
872
885
|
for (const glyph of line.glyphs) {
|
|
873
886
|
const path = await p.glyphPathProvider(glyph.id, glyph.fontDesc);
|
|
874
887
|
if (!path || path === "M 0 0") {
|
|
@@ -944,43 +957,45 @@ async function buildDrawOps(p) {
|
|
|
944
957
|
}
|
|
945
958
|
}
|
|
946
959
|
}
|
|
947
|
-
if (p.background
|
|
960
|
+
if (p.background || p.border) {
|
|
948
961
|
const bgX = 0;
|
|
949
962
|
const bgY = 0;
|
|
950
963
|
const bgWidth = p.canvas.width;
|
|
951
964
|
const bgHeight = p.canvas.height;
|
|
952
|
-
const
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
965
|
+
const borderWidth2 = p.border?.width ?? 0;
|
|
966
|
+
const borderRadius = p.border?.radius ?? 0;
|
|
967
|
+
const halfBorder = borderWidth2 / 2;
|
|
968
|
+
const maxRadius = Math.min(bgWidth - borderWidth2, bgHeight - borderWidth2) / 2;
|
|
969
|
+
const outerRadius = Math.min(borderRadius, maxRadius);
|
|
970
|
+
const innerRadius = Math.max(0, outerRadius - halfBorder);
|
|
971
|
+
if (p.background?.color) {
|
|
957
972
|
ops.push({
|
|
958
973
|
op: "Rectangle",
|
|
959
|
-
x: bgX +
|
|
960
|
-
y: bgY +
|
|
961
|
-
width: bgWidth -
|
|
962
|
-
height: bgHeight -
|
|
974
|
+
x: bgX + borderWidth2,
|
|
975
|
+
y: bgY + borderWidth2,
|
|
976
|
+
width: bgWidth - borderWidth2 * 2,
|
|
977
|
+
height: bgHeight - borderWidth2 * 2,
|
|
963
978
|
fill: { kind: "solid", color: p.background.color, opacity: p.background.opacity },
|
|
964
|
-
borderRadius:
|
|
979
|
+
borderRadius: innerRadius
|
|
965
980
|
});
|
|
966
981
|
}
|
|
967
|
-
if (p.
|
|
968
|
-
const borderRadius = p.background.border.borderRadius !== void 0 ? p.background.border.borderRadius : backgroundRadius;
|
|
982
|
+
if (p.border && p.border.width > 0) {
|
|
969
983
|
ops.push({
|
|
970
984
|
op: "RectangleStroke",
|
|
971
|
-
x: bgX,
|
|
972
|
-
y: bgY,
|
|
973
|
-
width: bgWidth,
|
|
974
|
-
height: bgHeight,
|
|
985
|
+
x: bgX + halfBorder,
|
|
986
|
+
y: bgY + halfBorder,
|
|
987
|
+
width: bgWidth - borderWidth2,
|
|
988
|
+
height: bgHeight - borderWidth2,
|
|
975
989
|
stroke: {
|
|
976
|
-
width: p.
|
|
977
|
-
color: p.
|
|
978
|
-
opacity: p.
|
|
990
|
+
width: p.border.width,
|
|
991
|
+
color: p.border.color,
|
|
992
|
+
opacity: p.border.opacity
|
|
979
993
|
},
|
|
980
|
-
borderRadius
|
|
994
|
+
borderRadius: outerRadius
|
|
981
995
|
});
|
|
982
996
|
}
|
|
983
997
|
}
|
|
998
|
+
ops.push(...highlighterOps);
|
|
984
999
|
ops.push(...textOps);
|
|
985
1000
|
return ops;
|
|
986
1001
|
}
|
|
@@ -1108,7 +1123,9 @@ function applyTypewriterAnimation(ops, lines, progress, style, fontSize, time, d
|
|
|
1108
1123
|
const wordSegments = getWordSegments(lines);
|
|
1109
1124
|
const totalWords = wordSegments.length;
|
|
1110
1125
|
const visibleWords = Math.floor(progress * totalWords);
|
|
1111
|
-
if (visibleWords === 0)
|
|
1126
|
+
if (visibleWords === 0) {
|
|
1127
|
+
return ops.filter((x) => x.op === "BeginFrame" || x.op === "Rectangle" || x.op === "RectangleStroke");
|
|
1128
|
+
}
|
|
1112
1129
|
let totalVisibleGlyphs = 0;
|
|
1113
1130
|
for (let i = 0; i < Math.min(visibleWords, wordSegments.length); i++) {
|
|
1114
1131
|
totalVisibleGlyphs += wordSegments[i].glyphCount;
|
|
@@ -1122,7 +1139,9 @@ function applyTypewriterAnimation(ops, lines, progress, style, fontSize, time, d
|
|
|
1122
1139
|
} else {
|
|
1123
1140
|
const totalGlyphs = lines.reduce((s, l) => s + l.glyphs.length, 0);
|
|
1124
1141
|
const visibleGlyphs = Math.floor(progress * totalGlyphs);
|
|
1125
|
-
if (visibleGlyphs === 0)
|
|
1142
|
+
if (visibleGlyphs === 0) {
|
|
1143
|
+
return ops.filter((x) => x.op === "BeginFrame" || x.op === "Rectangle" || x.op === "RectangleStroke");
|
|
1144
|
+
}
|
|
1126
1145
|
const visibleOpsRaw = sliceGlyphOps(ops, visibleGlyphs);
|
|
1127
1146
|
const visibleOps = progress >= DECORATION_DONE_THRESHOLD ? visibleOpsRaw : visibleOpsRaw.filter((o) => o.op !== "DecorationLine");
|
|
1128
1147
|
if (progress < 1 && visibleGlyphs > 0) {
|
|
@@ -1138,8 +1157,10 @@ function applyAscendAnimation(ops, lines, progress, direction, fontSize, duratio
|
|
|
1138
1157
|
const result = [];
|
|
1139
1158
|
let glyphIndex = 0;
|
|
1140
1159
|
for (const op of ops) {
|
|
1141
|
-
if (op.op === "BeginFrame") {
|
|
1160
|
+
if (op.op === "BeginFrame" || op.op === "Rectangle" || op.op === "RectangleStroke") {
|
|
1142
1161
|
result.push(op);
|
|
1162
|
+
}
|
|
1163
|
+
if (op.op === "FillPath" || op.op === "StrokePath") {
|
|
1143
1164
|
break;
|
|
1144
1165
|
}
|
|
1145
1166
|
}
|
|
@@ -1198,8 +1219,10 @@ function applyShiftAnimation(ops, lines, progress, direction, fontSize, style, d
|
|
|
1198
1219
|
if (totalUnits === 0) return ops;
|
|
1199
1220
|
const result = [];
|
|
1200
1221
|
for (const op of ops) {
|
|
1201
|
-
if (op.op === "BeginFrame") {
|
|
1222
|
+
if (op.op === "BeginFrame" || op.op === "Rectangle" || op.op === "RectangleStroke") {
|
|
1202
1223
|
result.push(op);
|
|
1224
|
+
}
|
|
1225
|
+
if (op.op === "FillPath" || op.op === "StrokePath") {
|
|
1203
1226
|
break;
|
|
1204
1227
|
}
|
|
1205
1228
|
}
|
|
@@ -1473,7 +1496,7 @@ function sliceGlyphOps(ops, maxGlyphs) {
|
|
|
1473
1496
|
let glyphCount = 0;
|
|
1474
1497
|
let foundGlyphs = false;
|
|
1475
1498
|
for (const op of ops) {
|
|
1476
|
-
if (op.op === "BeginFrame") {
|
|
1499
|
+
if (op.op === "BeginFrame" || op.op === "Rectangle" || op.op === "RectangleStroke") {
|
|
1477
1500
|
result.push(op);
|
|
1478
1501
|
continue;
|
|
1479
1502
|
}
|
|
@@ -1501,9 +1524,11 @@ function sliceGlyphOps(ops, maxGlyphs) {
|
|
|
1501
1524
|
return result;
|
|
1502
1525
|
}
|
|
1503
1526
|
function addTypewriterCursor(ops, glyphCount, fontSize, time) {
|
|
1504
|
-
|
|
1527
|
+
if (glyphCount === 0) return ops;
|
|
1528
|
+
const blinkRate = 1;
|
|
1505
1529
|
const cursorVisible = Math.floor(time * blinkRate * 2) % 2 === 0;
|
|
1506
|
-
|
|
1530
|
+
const alwaysShowCursor = true;
|
|
1531
|
+
if (!alwaysShowCursor && !cursorVisible) return ops;
|
|
1507
1532
|
let last = null;
|
|
1508
1533
|
let count = 0;
|
|
1509
1534
|
for (const op of ops) {
|
|
@@ -1519,13 +1544,16 @@ function addTypewriterCursor(ops, glyphCount, fontSize, time) {
|
|
|
1519
1544
|
const color = getTextColorFromOps(ops);
|
|
1520
1545
|
const cursorX = last.x + fontSize * 0.5;
|
|
1521
1546
|
const cursorY = last.y;
|
|
1547
|
+
const cursorWidth = Math.max(3, fontSize / 15);
|
|
1522
1548
|
const cursorOp = {
|
|
1523
1549
|
op: "DecorationLine",
|
|
1524
|
-
from: { x: cursorX, y: cursorY - fontSize * 0.
|
|
1525
|
-
|
|
1526
|
-
|
|
1550
|
+
from: { x: cursorX, y: cursorY - fontSize * 0.75 },
|
|
1551
|
+
// Slightly taller
|
|
1552
|
+
to: { x: cursorX, y: cursorY + fontSize * 0.15 },
|
|
1553
|
+
width: cursorWidth,
|
|
1527
1554
|
color,
|
|
1528
|
-
opacity: 1
|
|
1555
|
+
opacity: alwaysShowCursor ? 1 : cursorVisible ? 1 : 0
|
|
1556
|
+
// Always visible or blink
|
|
1529
1557
|
};
|
|
1530
1558
|
return [...ops, cursorOp];
|
|
1531
1559
|
}
|
|
@@ -2415,7 +2443,8 @@ async function createTextEngine(opts = {}) {
|
|
|
2415
2443
|
size: main.size,
|
|
2416
2444
|
weight: `${main.weight}`,
|
|
2417
2445
|
color: main.color,
|
|
2418
|
-
opacity: main.opacity
|
|
2446
|
+
opacity: main.opacity,
|
|
2447
|
+
background: main.background
|
|
2419
2448
|
},
|
|
2420
2449
|
style: {
|
|
2421
2450
|
lineHeight: asset.style?.lineHeight ?? 1.2,
|
|
@@ -2429,6 +2458,7 @@ async function createTextEngine(opts = {}) {
|
|
|
2429
2458
|
vertical: asset.align?.vertical ?? "middle"
|
|
2430
2459
|
},
|
|
2431
2460
|
background: asset.background,
|
|
2461
|
+
border: asset.border,
|
|
2432
2462
|
padding: asset.padding,
|
|
2433
2463
|
glyphPathProvider: (gid, fontDesc) => fonts.glyphPath(fontDesc || desc, gid),
|
|
2434
2464
|
getUnitsPerEm: (fontDesc) => fonts.getUnitsPerEm(fontDesc || desc)
|
|
@@ -2481,7 +2511,7 @@ async function createTextEngine(opts = {}) {
|
|
|
2481
2511
|
try {
|
|
2482
2512
|
const hasBackground = !!asset.background?.color;
|
|
2483
2513
|
const hasAnimation = !!asset.animation?.preset;
|
|
2484
|
-
const hasBorderRadius = (asset.
|
|
2514
|
+
const hasBorderRadius = (asset.border?.radius ?? 0) > 0;
|
|
2485
2515
|
const needsAlpha = !hasBackground && hasAnimation || hasBorderRadius;
|
|
2486
2516
|
console.log(
|
|
2487
2517
|
`\u{1F3A8} Video settings: Animation=${hasAnimation}, Background=${hasBackground}, BorderRadius=${hasBorderRadius}, Alpha=${needsAlpha}`
|
package/dist/entry.web.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ type RichTextValidated = Required<{
|
|
|
33
33
|
weight: string | number;
|
|
34
34
|
color: string;
|
|
35
35
|
opacity: number;
|
|
36
|
+
background?: string;
|
|
36
37
|
};
|
|
37
38
|
style?: {
|
|
38
39
|
letterSpacing: number;
|
|
@@ -63,14 +64,12 @@ type RichTextValidated = Required<{
|
|
|
63
64
|
background?: {
|
|
64
65
|
color?: string;
|
|
65
66
|
opacity: number;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
borderRadius?: number;
|
|
73
|
-
};
|
|
67
|
+
};
|
|
68
|
+
border?: {
|
|
69
|
+
width: number;
|
|
70
|
+
color: string;
|
|
71
|
+
opacity: number;
|
|
72
|
+
radius: number;
|
|
74
73
|
};
|
|
75
74
|
padding?: number | {
|
|
76
75
|
top: number;
|
package/dist/entry.web.js
CHANGED
|
@@ -66,7 +66,8 @@ var fontSchema = Joi.object({
|
|
|
66
66
|
size: Joi.number().min(CANVAS_CONFIG.LIMITS.minFontSize).max(CANVAS_CONFIG.LIMITS.maxFontSize).default(CANVAS_CONFIG.DEFAULTS.fontSize),
|
|
67
67
|
weight: Joi.alternatives().try(Joi.string(), Joi.number()).default("400"),
|
|
68
68
|
color: Joi.string().pattern(HEX6).default(CANVAS_CONFIG.DEFAULTS.color),
|
|
69
|
-
opacity: Joi.number().min(0).max(1).default(1)
|
|
69
|
+
opacity: Joi.number().min(0).max(1).default(1),
|
|
70
|
+
background: Joi.string().pattern(HEX6).optional()
|
|
70
71
|
}).unknown(false);
|
|
71
72
|
var styleSchema = Joi.object({
|
|
72
73
|
letterSpacing: Joi.number().default(0),
|
|
@@ -102,15 +103,11 @@ var borderSchema = Joi.object({
|
|
|
102
103
|
width: Joi.number().min(0).default(0),
|
|
103
104
|
color: Joi.string().pattern(HEX6).default("#000000"),
|
|
104
105
|
opacity: Joi.number().min(0).max(1).default(1),
|
|
105
|
-
|
|
106
|
+
radius: Joi.number().min(0).default(0)
|
|
106
107
|
}).unknown(false);
|
|
107
108
|
var backgroundSchema = Joi.object({
|
|
108
109
|
color: Joi.string().pattern(HEX6).optional(),
|
|
109
|
-
opacity: Joi.number().min(0).max(1).default(1)
|
|
110
|
-
backgroundRadius: Joi.number().min(0).default(0),
|
|
111
|
-
borderRadius: Joi.number().min(0).optional(),
|
|
112
|
-
// Deprecated: use backgroundRadius
|
|
113
|
-
border: borderSchema.optional()
|
|
110
|
+
opacity: Joi.number().min(0).max(1).default(1)
|
|
114
111
|
}).unknown(false);
|
|
115
112
|
var paddingSchema = Joi.alternatives().try(
|
|
116
113
|
Joi.number().min(0).default(0),
|
|
@@ -138,6 +135,7 @@ var RichTextAssetSchema = Joi.object({
|
|
|
138
135
|
stroke: strokeSchema.optional(),
|
|
139
136
|
shadow: shadowSchema.optional(),
|
|
140
137
|
background: backgroundSchema.optional(),
|
|
138
|
+
border: borderSchema.optional(),
|
|
141
139
|
padding: paddingSchema.optional(),
|
|
142
140
|
align: alignmentSchema.optional(),
|
|
143
141
|
animation: animationSchema.optional(),
|
|
@@ -823,7 +821,7 @@ function normalizePadding(padding) {
|
|
|
823
821
|
async function buildDrawOps(p) {
|
|
824
822
|
const ops = [];
|
|
825
823
|
const padding = normalizePadding(p.padding);
|
|
826
|
-
const borderWidth = p.
|
|
824
|
+
const borderWidth = p.border?.width ?? 0;
|
|
827
825
|
ops.push({
|
|
828
826
|
op: "BeginFrame",
|
|
829
827
|
width: p.canvas.width,
|
|
@@ -856,6 +854,7 @@ async function buildDrawOps(p) {
|
|
|
856
854
|
const fill = p.style.gradient ? gradientSpecFrom(p.style.gradient, 1) : { kind: "solid", color: p.font.color, opacity: p.font.opacity };
|
|
857
855
|
const decoColor = p.style.gradient ? p.style.gradient.stops[p.style.gradient.stops.length - 1]?.color ?? p.font.color : p.font.color;
|
|
858
856
|
const textOps = [];
|
|
857
|
+
const highlighterOps = [];
|
|
859
858
|
let gMinX = Infinity, gMinY = Infinity, gMaxX = -Infinity, gMaxY = -Infinity;
|
|
860
859
|
for (const line of p.lines) {
|
|
861
860
|
let lineX;
|
|
@@ -874,6 +873,20 @@ async function buildDrawOps(p) {
|
|
|
874
873
|
let xCursor = lineX;
|
|
875
874
|
const lineIndex = p.lines.indexOf(line);
|
|
876
875
|
const baselineY = blockY + lineIndex * lineHeightPx;
|
|
876
|
+
if (p.font.background) {
|
|
877
|
+
const highlightPadding = p.font.size * 0.15;
|
|
878
|
+
const verticalPadding = p.font.size * 0.12;
|
|
879
|
+
const highlightHeight = p.font.size * 0.92 + verticalPadding * 2;
|
|
880
|
+
const highlightY = baselineY - p.font.size * 0.78 - verticalPadding;
|
|
881
|
+
highlighterOps.push({
|
|
882
|
+
op: "Rectangle",
|
|
883
|
+
x: lineX - highlightPadding,
|
|
884
|
+
y: highlightY,
|
|
885
|
+
width: line.width + highlightPadding * 2,
|
|
886
|
+
height: highlightHeight,
|
|
887
|
+
fill: { kind: "solid", color: p.font.background, opacity: 1 }
|
|
888
|
+
});
|
|
889
|
+
}
|
|
877
890
|
for (const glyph of line.glyphs) {
|
|
878
891
|
const path = await p.glyphPathProvider(glyph.id, glyph.fontDesc);
|
|
879
892
|
if (!path || path === "M 0 0") {
|
|
@@ -949,43 +962,45 @@ async function buildDrawOps(p) {
|
|
|
949
962
|
}
|
|
950
963
|
}
|
|
951
964
|
}
|
|
952
|
-
if (p.background
|
|
965
|
+
if (p.background || p.border) {
|
|
953
966
|
const bgX = 0;
|
|
954
967
|
const bgY = 0;
|
|
955
968
|
const bgWidth = p.canvas.width;
|
|
956
969
|
const bgHeight = p.canvas.height;
|
|
957
|
-
const
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
970
|
+
const borderWidth2 = p.border?.width ?? 0;
|
|
971
|
+
const borderRadius = p.border?.radius ?? 0;
|
|
972
|
+
const halfBorder = borderWidth2 / 2;
|
|
973
|
+
const maxRadius = Math.min(bgWidth - borderWidth2, bgHeight - borderWidth2) / 2;
|
|
974
|
+
const outerRadius = Math.min(borderRadius, maxRadius);
|
|
975
|
+
const innerRadius = Math.max(0, outerRadius - halfBorder);
|
|
976
|
+
if (p.background?.color) {
|
|
962
977
|
ops.push({
|
|
963
978
|
op: "Rectangle",
|
|
964
|
-
x: bgX +
|
|
965
|
-
y: bgY +
|
|
966
|
-
width: bgWidth -
|
|
967
|
-
height: bgHeight -
|
|
979
|
+
x: bgX + borderWidth2,
|
|
980
|
+
y: bgY + borderWidth2,
|
|
981
|
+
width: bgWidth - borderWidth2 * 2,
|
|
982
|
+
height: bgHeight - borderWidth2 * 2,
|
|
968
983
|
fill: { kind: "solid", color: p.background.color, opacity: p.background.opacity },
|
|
969
|
-
borderRadius:
|
|
984
|
+
borderRadius: innerRadius
|
|
970
985
|
});
|
|
971
986
|
}
|
|
972
|
-
if (p.
|
|
973
|
-
const borderRadius = p.background.border.borderRadius !== void 0 ? p.background.border.borderRadius : backgroundRadius;
|
|
987
|
+
if (p.border && p.border.width > 0) {
|
|
974
988
|
ops.push({
|
|
975
989
|
op: "RectangleStroke",
|
|
976
|
-
x: bgX,
|
|
977
|
-
y: bgY,
|
|
978
|
-
width: bgWidth,
|
|
979
|
-
height: bgHeight,
|
|
990
|
+
x: bgX + halfBorder,
|
|
991
|
+
y: bgY + halfBorder,
|
|
992
|
+
width: bgWidth - borderWidth2,
|
|
993
|
+
height: bgHeight - borderWidth2,
|
|
980
994
|
stroke: {
|
|
981
|
-
width: p.
|
|
982
|
-
color: p.
|
|
983
|
-
opacity: p.
|
|
995
|
+
width: p.border.width,
|
|
996
|
+
color: p.border.color,
|
|
997
|
+
opacity: p.border.opacity
|
|
984
998
|
},
|
|
985
|
-
borderRadius
|
|
999
|
+
borderRadius: outerRadius
|
|
986
1000
|
});
|
|
987
1001
|
}
|
|
988
1002
|
}
|
|
1003
|
+
ops.push(...highlighterOps);
|
|
989
1004
|
ops.push(...textOps);
|
|
990
1005
|
return ops;
|
|
991
1006
|
}
|
|
@@ -1113,7 +1128,9 @@ function applyTypewriterAnimation(ops, lines, progress, style, fontSize, time, d
|
|
|
1113
1128
|
const wordSegments = getWordSegments(lines);
|
|
1114
1129
|
const totalWords = wordSegments.length;
|
|
1115
1130
|
const visibleWords = Math.floor(progress * totalWords);
|
|
1116
|
-
if (visibleWords === 0)
|
|
1131
|
+
if (visibleWords === 0) {
|
|
1132
|
+
return ops.filter((x) => x.op === "BeginFrame" || x.op === "Rectangle" || x.op === "RectangleStroke");
|
|
1133
|
+
}
|
|
1117
1134
|
let totalVisibleGlyphs = 0;
|
|
1118
1135
|
for (let i = 0; i < Math.min(visibleWords, wordSegments.length); i++) {
|
|
1119
1136
|
totalVisibleGlyphs += wordSegments[i].glyphCount;
|
|
@@ -1127,7 +1144,9 @@ function applyTypewriterAnimation(ops, lines, progress, style, fontSize, time, d
|
|
|
1127
1144
|
} else {
|
|
1128
1145
|
const totalGlyphs = lines.reduce((s, l) => s + l.glyphs.length, 0);
|
|
1129
1146
|
const visibleGlyphs = Math.floor(progress * totalGlyphs);
|
|
1130
|
-
if (visibleGlyphs === 0)
|
|
1147
|
+
if (visibleGlyphs === 0) {
|
|
1148
|
+
return ops.filter((x) => x.op === "BeginFrame" || x.op === "Rectangle" || x.op === "RectangleStroke");
|
|
1149
|
+
}
|
|
1131
1150
|
const visibleOpsRaw = sliceGlyphOps(ops, visibleGlyphs);
|
|
1132
1151
|
const visibleOps = progress >= DECORATION_DONE_THRESHOLD ? visibleOpsRaw : visibleOpsRaw.filter((o) => o.op !== "DecorationLine");
|
|
1133
1152
|
if (progress < 1 && visibleGlyphs > 0) {
|
|
@@ -1143,8 +1162,10 @@ function applyAscendAnimation(ops, lines, progress, direction, fontSize, duratio
|
|
|
1143
1162
|
const result = [];
|
|
1144
1163
|
let glyphIndex = 0;
|
|
1145
1164
|
for (const op of ops) {
|
|
1146
|
-
if (op.op === "BeginFrame") {
|
|
1165
|
+
if (op.op === "BeginFrame" || op.op === "Rectangle" || op.op === "RectangleStroke") {
|
|
1147
1166
|
result.push(op);
|
|
1167
|
+
}
|
|
1168
|
+
if (op.op === "FillPath" || op.op === "StrokePath") {
|
|
1148
1169
|
break;
|
|
1149
1170
|
}
|
|
1150
1171
|
}
|
|
@@ -1203,8 +1224,10 @@ function applyShiftAnimation(ops, lines, progress, direction, fontSize, style, d
|
|
|
1203
1224
|
if (totalUnits === 0) return ops;
|
|
1204
1225
|
const result = [];
|
|
1205
1226
|
for (const op of ops) {
|
|
1206
|
-
if (op.op === "BeginFrame") {
|
|
1227
|
+
if (op.op === "BeginFrame" || op.op === "Rectangle" || op.op === "RectangleStroke") {
|
|
1207
1228
|
result.push(op);
|
|
1229
|
+
}
|
|
1230
|
+
if (op.op === "FillPath" || op.op === "StrokePath") {
|
|
1208
1231
|
break;
|
|
1209
1232
|
}
|
|
1210
1233
|
}
|
|
@@ -1478,7 +1501,7 @@ function sliceGlyphOps(ops, maxGlyphs) {
|
|
|
1478
1501
|
let glyphCount = 0;
|
|
1479
1502
|
let foundGlyphs = false;
|
|
1480
1503
|
for (const op of ops) {
|
|
1481
|
-
if (op.op === "BeginFrame") {
|
|
1504
|
+
if (op.op === "BeginFrame" || op.op === "Rectangle" || op.op === "RectangleStroke") {
|
|
1482
1505
|
result.push(op);
|
|
1483
1506
|
continue;
|
|
1484
1507
|
}
|
|
@@ -1506,9 +1529,11 @@ function sliceGlyphOps(ops, maxGlyphs) {
|
|
|
1506
1529
|
return result;
|
|
1507
1530
|
}
|
|
1508
1531
|
function addTypewriterCursor(ops, glyphCount, fontSize, time) {
|
|
1509
|
-
|
|
1532
|
+
if (glyphCount === 0) return ops;
|
|
1533
|
+
const blinkRate = 1;
|
|
1510
1534
|
const cursorVisible = Math.floor(time * blinkRate * 2) % 2 === 0;
|
|
1511
|
-
|
|
1535
|
+
const alwaysShowCursor = true;
|
|
1536
|
+
if (!alwaysShowCursor && !cursorVisible) return ops;
|
|
1512
1537
|
let last = null;
|
|
1513
1538
|
let count = 0;
|
|
1514
1539
|
for (const op of ops) {
|
|
@@ -1524,13 +1549,16 @@ function addTypewriterCursor(ops, glyphCount, fontSize, time) {
|
|
|
1524
1549
|
const color = getTextColorFromOps(ops);
|
|
1525
1550
|
const cursorX = last.x + fontSize * 0.5;
|
|
1526
1551
|
const cursorY = last.y;
|
|
1552
|
+
const cursorWidth = Math.max(3, fontSize / 15);
|
|
1527
1553
|
const cursorOp = {
|
|
1528
1554
|
op: "DecorationLine",
|
|
1529
|
-
from: { x: cursorX, y: cursorY - fontSize * 0.
|
|
1530
|
-
|
|
1531
|
-
|
|
1555
|
+
from: { x: cursorX, y: cursorY - fontSize * 0.75 },
|
|
1556
|
+
// Slightly taller
|
|
1557
|
+
to: { x: cursorX, y: cursorY + fontSize * 0.15 },
|
|
1558
|
+
width: cursorWidth,
|
|
1532
1559
|
color,
|
|
1533
|
-
opacity: 1
|
|
1560
|
+
opacity: alwaysShowCursor ? 1 : cursorVisible ? 1 : 0
|
|
1561
|
+
// Always visible or blink
|
|
1534
1562
|
};
|
|
1535
1563
|
return [...ops, cursorOp];
|
|
1536
1564
|
}
|
|
@@ -2139,7 +2167,8 @@ async function createTextEngine(opts = {}) {
|
|
|
2139
2167
|
size: main.size,
|
|
2140
2168
|
weight: `${main.weight}`,
|
|
2141
2169
|
color: main.color,
|
|
2142
|
-
opacity: main.opacity
|
|
2170
|
+
opacity: main.opacity,
|
|
2171
|
+
background: main.background
|
|
2143
2172
|
},
|
|
2144
2173
|
style: {
|
|
2145
2174
|
lineHeight: asset.style?.lineHeight ?? 1.2,
|
|
@@ -2153,6 +2182,7 @@ async function createTextEngine(opts = {}) {
|
|
|
2153
2182
|
vertical: asset.align?.vertical ?? "middle"
|
|
2154
2183
|
},
|
|
2155
2184
|
background: asset.background,
|
|
2185
|
+
border: asset.border,
|
|
2156
2186
|
padding: asset.padding,
|
|
2157
2187
|
glyphPathProvider: (gid, fontDesc) => fonts.glyphPath(fontDesc || desc, gid),
|
|
2158
2188
|
getUnitsPerEm: (fontDesc) => fonts.getUnitsPerEm(fontDesc || desc)
|
package/package.json
CHANGED