@clypra/engine 1.1.1 → 1.2.0
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/index.cjs +501 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +157 -1
- package/dist/index.d.ts +157 -1
- package/dist/index.js +493 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -386,6 +386,46 @@ function _resetPlatformCache() {
|
|
|
386
386
|
_offscreenCanvas = null;
|
|
387
387
|
_webgl2 = null;
|
|
388
388
|
}
|
|
389
|
+
var CanvasDevice = class {
|
|
390
|
+
static canvases = [];
|
|
391
|
+
static maxPoolSize = 10;
|
|
392
|
+
/**
|
|
393
|
+
* Acquire a Canvas context from the pool or create a new one.
|
|
394
|
+
* If a canvas is pulled from the pool, it is resized to the target dimensions.
|
|
395
|
+
*/
|
|
396
|
+
static acquire(width, height) {
|
|
397
|
+
let canvas;
|
|
398
|
+
if (this.canvases.length > 0) {
|
|
399
|
+
canvas = this.canvases.pop();
|
|
400
|
+
if (canvas.width !== width || canvas.height !== height) {
|
|
401
|
+
canvas.width = width;
|
|
402
|
+
canvas.height = height;
|
|
403
|
+
}
|
|
404
|
+
} else {
|
|
405
|
+
canvas = createCanvas(width, height);
|
|
406
|
+
}
|
|
407
|
+
return canvas;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Release a canvas back to the pool, or free its resources immediately if pool is full.
|
|
411
|
+
*/
|
|
412
|
+
static release(canvas) {
|
|
413
|
+
if (this.canvases.length < this.maxPoolSize) {
|
|
414
|
+
this.canvases.push(canvas);
|
|
415
|
+
} else {
|
|
416
|
+
releaseCanvas(canvas);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Disposes all pooled canvases to release GPU/memory backing stores.
|
|
421
|
+
*/
|
|
422
|
+
static clearPool() {
|
|
423
|
+
while (this.canvases.length > 0) {
|
|
424
|
+
const c = this.canvases.pop();
|
|
425
|
+
releaseCanvas(c);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
};
|
|
389
429
|
|
|
390
430
|
// src/engine/procedural/utils.ts
|
|
391
431
|
function getCanvas2DContext(canvas) {
|
|
@@ -777,6 +817,17 @@ function restoreLetterSpacing(ctx, saved) {
|
|
|
777
817
|
function getCanvas2DContext2(canvas) {
|
|
778
818
|
return canvas.getContext("2d");
|
|
779
819
|
}
|
|
820
|
+
function ctxSupportsFilter(ctx) {
|
|
821
|
+
try {
|
|
822
|
+
const prev = ctx.filter;
|
|
823
|
+
ctx.filter = "blur(4px)";
|
|
824
|
+
const ok = typeof ctx.filter === "string" && ctx.filter.includes("blur");
|
|
825
|
+
ctx.filter = prev;
|
|
826
|
+
return ok;
|
|
827
|
+
} catch {
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
780
831
|
function renderTextEffectCore(ctx, cfg) {
|
|
781
832
|
if (cfg.customRenderer === "InkBrushEngine") {
|
|
782
833
|
const engine = new InkBrushEngine(cfg);
|
|
@@ -1086,19 +1137,31 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
1086
1137
|
const vpy = cHeight / 2 + (bevelVanishingPointY !== void 0 ? bevelVanishingPointY : 80) / 100 * (cHeight / 2);
|
|
1087
1138
|
const fl = Math.max(100, bevelFocalLength !== void 0 ? bevelFocalLength : 400);
|
|
1088
1139
|
if (bevelBlur && bevelBlur > 0) {
|
|
1089
|
-
ctx.save();
|
|
1090
|
-
ctx.filter = `blur(${bevelBlur}px)`;
|
|
1091
1140
|
const blurColor = bevelBlurColor || bevelShadow || "#000000";
|
|
1092
|
-
|
|
1093
|
-
const scale = fl / (fl + i);
|
|
1141
|
+
if (ctxSupportsFilter(ctx)) {
|
|
1094
1142
|
ctx.save();
|
|
1095
|
-
ctx.
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1143
|
+
ctx.filter = `blur(${bevelBlur}px)`;
|
|
1144
|
+
for (let i = bevelDepth; i > 0; i -= Math.max(1, Math.floor(bevelDepth / 4))) {
|
|
1145
|
+
const scale = fl / (fl + i);
|
|
1146
|
+
ctx.save();
|
|
1147
|
+
ctx.translate(vpx, vpy);
|
|
1148
|
+
ctx.scale(scale, scale);
|
|
1149
|
+
ctx.translate(-vpx, -vpy);
|
|
1150
|
+
renderLines("fill", blurColor);
|
|
1151
|
+
ctx.restore();
|
|
1152
|
+
}
|
|
1099
1153
|
ctx.restore();
|
|
1154
|
+
} else {
|
|
1155
|
+
for (let i = bevelDepth; i > 0; i -= Math.max(1, Math.floor(bevelDepth / 4))) {
|
|
1156
|
+
const scale = fl / (fl + i);
|
|
1157
|
+
ctx.save();
|
|
1158
|
+
ctx.translate(vpx, vpy);
|
|
1159
|
+
ctx.scale(scale, scale);
|
|
1160
|
+
ctx.translate(-vpx, -vpy);
|
|
1161
|
+
renderWithShadowTrick("fill", blurColor, bevelBlur, 0, 0, 100);
|
|
1162
|
+
ctx.restore();
|
|
1163
|
+
}
|
|
1100
1164
|
}
|
|
1101
|
-
ctx.restore();
|
|
1102
1165
|
}
|
|
1103
1166
|
ctx.save();
|
|
1104
1167
|
for (let i = bevelDepth; i > 0; i--) {
|
|
@@ -1145,14 +1208,21 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
1145
1208
|
return { dx: i, dy: i };
|
|
1146
1209
|
};
|
|
1147
1210
|
if (bevelBlur && bevelBlur > 0) {
|
|
1148
|
-
ctx.save();
|
|
1149
|
-
ctx.filter = `blur(${bevelBlur}px)`;
|
|
1150
1211
|
const blurColor = bevelBlurColor || bevelShadow || "#000000";
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1212
|
+
if (ctxSupportsFilter(ctx)) {
|
|
1213
|
+
ctx.save();
|
|
1214
|
+
ctx.filter = `blur(${bevelBlur}px)`;
|
|
1215
|
+
for (let i = bevelDepth; i > 0; i -= Math.max(1, Math.floor(bevelDepth / 4))) {
|
|
1216
|
+
const { dx, dy } = getDirOffset(i);
|
|
1217
|
+
renderLines("fill", blurColor, dx, dy);
|
|
1218
|
+
}
|
|
1219
|
+
ctx.restore();
|
|
1220
|
+
} else {
|
|
1221
|
+
for (let i = bevelDepth; i > 0; i -= Math.max(1, Math.floor(bevelDepth / 4))) {
|
|
1222
|
+
const { dx, dy } = getDirOffset(i);
|
|
1223
|
+
renderWithShadowTrick("fill", blurColor, bevelBlur, dx, dy, 100);
|
|
1224
|
+
}
|
|
1154
1225
|
}
|
|
1155
|
-
ctx.restore();
|
|
1156
1226
|
}
|
|
1157
1227
|
ctx.save();
|
|
1158
1228
|
for (let i = bevelDepth; i > 0; i--) {
|
|
@@ -1206,24 +1276,51 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
1206
1276
|
customStrokeStyle = grad;
|
|
1207
1277
|
}
|
|
1208
1278
|
const drawStrokeLayer = (color, width, blurAmount, opacity, position) => {
|
|
1209
|
-
ctx
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1279
|
+
if (blurAmount > 0 && ctxSupportsFilter(ctx)) {
|
|
1280
|
+
ctx.save();
|
|
1281
|
+
ctx.globalAlpha = opacity / 100;
|
|
1282
|
+
ctx.strokeStyle = color;
|
|
1213
1283
|
ctx.filter = `blur(${blurAmount}px)`;
|
|
1284
|
+
if (position === "outside") {
|
|
1285
|
+
ctx.lineWidth = width * 2;
|
|
1286
|
+
renderLines("stroke");
|
|
1287
|
+
} else if (position === "center") {
|
|
1288
|
+
ctx.lineWidth = width;
|
|
1289
|
+
renderLines("stroke");
|
|
1290
|
+
} else if (position === "inside") {
|
|
1291
|
+
ctx.globalCompositeOperation = "source-atop";
|
|
1292
|
+
ctx.lineWidth = width * 2;
|
|
1293
|
+
renderLines("stroke");
|
|
1294
|
+
}
|
|
1295
|
+
ctx.restore();
|
|
1296
|
+
} else if (blurAmount > 0) {
|
|
1297
|
+
const colorStr = typeof color === "string" ? color : strokeColor;
|
|
1298
|
+
const spread = position === "center" ? width / 2 : width;
|
|
1299
|
+
if (position === "inside") {
|
|
1300
|
+
ctx.save();
|
|
1301
|
+
ctx.globalCompositeOperation = "source-atop";
|
|
1302
|
+
renderWithShadowTrick("stroke", colorStr, blurAmount, 0, 0, opacity, void 0, spread);
|
|
1303
|
+
ctx.restore();
|
|
1304
|
+
} else {
|
|
1305
|
+
renderWithShadowTrick("stroke", colorStr, blurAmount, 0, 0, opacity, void 0, spread);
|
|
1306
|
+
}
|
|
1307
|
+
} else {
|
|
1308
|
+
ctx.save();
|
|
1309
|
+
ctx.globalAlpha = opacity / 100;
|
|
1310
|
+
ctx.strokeStyle = color;
|
|
1311
|
+
if (position === "outside") {
|
|
1312
|
+
ctx.lineWidth = width * 2;
|
|
1313
|
+
renderLines("stroke");
|
|
1314
|
+
} else if (position === "center") {
|
|
1315
|
+
ctx.lineWidth = width;
|
|
1316
|
+
renderLines("stroke");
|
|
1317
|
+
} else if (position === "inside") {
|
|
1318
|
+
ctx.globalCompositeOperation = "source-atop";
|
|
1319
|
+
ctx.lineWidth = width * 2;
|
|
1320
|
+
renderLines("stroke");
|
|
1321
|
+
}
|
|
1322
|
+
ctx.restore();
|
|
1214
1323
|
}
|
|
1215
|
-
if (position === "outside") {
|
|
1216
|
-
ctx.lineWidth = width * 2;
|
|
1217
|
-
renderLines("stroke");
|
|
1218
|
-
} else if (position === "center") {
|
|
1219
|
-
ctx.lineWidth = width;
|
|
1220
|
-
renderLines("stroke");
|
|
1221
|
-
} else if (position === "inside") {
|
|
1222
|
-
ctx.globalCompositeOperation = "source-atop";
|
|
1223
|
-
ctx.lineWidth = width * 2;
|
|
1224
|
-
renderLines("stroke");
|
|
1225
|
-
}
|
|
1226
|
-
ctx.restore();
|
|
1227
1324
|
};
|
|
1228
1325
|
if (sType === "double") {
|
|
1229
1326
|
const outerWidth = strokeWidth + sWidthSecondary;
|
|
@@ -2348,6 +2445,146 @@ function checkFontVariant(variantName) {
|
|
|
2348
2445
|
if (typeof document === "undefined" || !document.fonts) return false;
|
|
2349
2446
|
return document.fonts.check(`16px "${variantName}"`);
|
|
2350
2447
|
}
|
|
2448
|
+
var FontLoader = class {
|
|
2449
|
+
state = {
|
|
2450
|
+
loading: /* @__PURE__ */ new Set(),
|
|
2451
|
+
loaded: /* @__PURE__ */ new Set(),
|
|
2452
|
+
failed: /* @__PURE__ */ new Map(),
|
|
2453
|
+
promises: /* @__PURE__ */ new Map()
|
|
2454
|
+
};
|
|
2455
|
+
async ensureFont(descriptor) {
|
|
2456
|
+
const key = this.getFontKey(descriptor);
|
|
2457
|
+
if (this.state.loaded.has(key)) {
|
|
2458
|
+
return {
|
|
2459
|
+
font: descriptor,
|
|
2460
|
+
loaded: true,
|
|
2461
|
+
loadTimeMs: 0
|
|
2462
|
+
};
|
|
2463
|
+
}
|
|
2464
|
+
if (this.state.failed.has(key)) {
|
|
2465
|
+
return {
|
|
2466
|
+
font: descriptor,
|
|
2467
|
+
loaded: false,
|
|
2468
|
+
error: this.state.failed.get(key),
|
|
2469
|
+
loadTimeMs: 0
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
if (this.state.promises.has(key)) {
|
|
2473
|
+
return this.state.promises.get(key);
|
|
2474
|
+
}
|
|
2475
|
+
const promise = this.loadFont(descriptor);
|
|
2476
|
+
this.state.promises.set(key, promise);
|
|
2477
|
+
return promise;
|
|
2478
|
+
}
|
|
2479
|
+
async ensureFonts(descriptors) {
|
|
2480
|
+
return Promise.all(descriptors.map((desc) => this.ensureFont(desc)));
|
|
2481
|
+
}
|
|
2482
|
+
async waitForFontsReady() {
|
|
2483
|
+
if (typeof document === "undefined" || !document.fonts) {
|
|
2484
|
+
return;
|
|
2485
|
+
}
|
|
2486
|
+
await document.fonts.ready;
|
|
2487
|
+
}
|
|
2488
|
+
isLoaded(descriptor) {
|
|
2489
|
+
const key = this.getFontKey(descriptor);
|
|
2490
|
+
return this.state.loaded.has(key);
|
|
2491
|
+
}
|
|
2492
|
+
getStats() {
|
|
2493
|
+
return {
|
|
2494
|
+
loaded: this.state.loaded.size,
|
|
2495
|
+
loading: this.state.loading.size,
|
|
2496
|
+
failed: this.state.failed.size
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
clear() {
|
|
2500
|
+
this.state.loading.clear();
|
|
2501
|
+
this.state.loaded.clear();
|
|
2502
|
+
this.state.failed.clear();
|
|
2503
|
+
this.state.promises.clear();
|
|
2504
|
+
}
|
|
2505
|
+
async loadFont(descriptor) {
|
|
2506
|
+
const key = this.getFontKey(descriptor);
|
|
2507
|
+
const startTime = performance.now();
|
|
2508
|
+
this.state.loading.add(key);
|
|
2509
|
+
try {
|
|
2510
|
+
if (typeof document === "undefined" || !document.fonts) {
|
|
2511
|
+
throw new Error("Font API not available");
|
|
2512
|
+
}
|
|
2513
|
+
const weight = this.normalizeFontWeight(descriptor.weight);
|
|
2514
|
+
const style = descriptor.style || "normal";
|
|
2515
|
+
const fontFace = `${style} ${weight} 16px "${descriptor.family}"`;
|
|
2516
|
+
if (document.fonts.check(fontFace)) {
|
|
2517
|
+
this.state.loaded.add(key);
|
|
2518
|
+
this.state.loading.delete(key);
|
|
2519
|
+
this.state.promises.delete(key);
|
|
2520
|
+
return {
|
|
2521
|
+
font: descriptor,
|
|
2522
|
+
loaded: true,
|
|
2523
|
+
loadTimeMs: performance.now() - startTime
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
await document.fonts.load(fontFace);
|
|
2527
|
+
if (!document.fonts.check(fontFace)) {
|
|
2528
|
+
throw new Error(`Font "${descriptor.family}" failed to load`);
|
|
2529
|
+
}
|
|
2530
|
+
this.state.loaded.add(key);
|
|
2531
|
+
this.state.loading.delete(key);
|
|
2532
|
+
this.state.promises.delete(key);
|
|
2533
|
+
return {
|
|
2534
|
+
font: descriptor,
|
|
2535
|
+
loaded: true,
|
|
2536
|
+
loadTimeMs: performance.now() - startTime
|
|
2537
|
+
};
|
|
2538
|
+
} catch (error) {
|
|
2539
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2540
|
+
this.state.failed.set(key, errorMessage);
|
|
2541
|
+
this.state.loading.delete(key);
|
|
2542
|
+
this.state.promises.delete(key);
|
|
2543
|
+
return {
|
|
2544
|
+
font: descriptor,
|
|
2545
|
+
loaded: false,
|
|
2546
|
+
error: errorMessage,
|
|
2547
|
+
loadTimeMs: performance.now() - startTime
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
getFontKey(descriptor) {
|
|
2552
|
+
const weight = this.normalizeFontWeight(descriptor.weight);
|
|
2553
|
+
const style = descriptor.style || "normal";
|
|
2554
|
+
return `${descriptor.family}|${weight}|${style}`;
|
|
2555
|
+
}
|
|
2556
|
+
normalizeFontWeight(weight) {
|
|
2557
|
+
if (typeof weight === "number") {
|
|
2558
|
+
return weight;
|
|
2559
|
+
}
|
|
2560
|
+
if (!weight) return 400;
|
|
2561
|
+
const asNum = parseInt(weight, 10);
|
|
2562
|
+
if (!isNaN(asNum) && asNum >= 100 && asNum <= 900) {
|
|
2563
|
+
return asNum;
|
|
2564
|
+
}
|
|
2565
|
+
const weightMap = {
|
|
2566
|
+
normal: 400,
|
|
2567
|
+
bold: 700,
|
|
2568
|
+
lighter: 300,
|
|
2569
|
+
bolder: 700
|
|
2570
|
+
};
|
|
2571
|
+
return weightMap[weight] ?? 400;
|
|
2572
|
+
}
|
|
2573
|
+
};
|
|
2574
|
+
var globalFontLoader = null;
|
|
2575
|
+
function getFontLoader() {
|
|
2576
|
+
if (!globalFontLoader) {
|
|
2577
|
+
globalFontLoader = new FontLoader();
|
|
2578
|
+
}
|
|
2579
|
+
return globalFontLoader;
|
|
2580
|
+
}
|
|
2581
|
+
function resetFontLoader() {
|
|
2582
|
+
globalFontLoader = null;
|
|
2583
|
+
}
|
|
2584
|
+
async function ensureFontsLoaded(descriptors) {
|
|
2585
|
+
const loader = getFontLoader();
|
|
2586
|
+
return loader.ensureFonts(descriptors);
|
|
2587
|
+
}
|
|
2351
2588
|
|
|
2352
2589
|
// src/engine/timelineDefaults.ts
|
|
2353
2590
|
function ensureDefaultTimeline(doc) {
|
|
@@ -2719,6 +2956,212 @@ function mergeSceneIntoConfig(doc, base) {
|
|
|
2719
2956
|
const out = sceneToConfig({ ...doc, legacyConfig: base });
|
|
2720
2957
|
return out;
|
|
2721
2958
|
}
|
|
2959
|
+
function resolveFontFamilyName(fontFamily) {
|
|
2960
|
+
const f = fontFamily?.toLowerCase() || "";
|
|
2961
|
+
if (f.includes("inter")) return "Inter Variable";
|
|
2962
|
+
if (f.includes("montserrat")) return "Montserrat Variable";
|
|
2963
|
+
if (f.includes("geist")) return "Geist Variable";
|
|
2964
|
+
if (f.includes("space grotesk") || f.includes("grotesk")) return "Space Grotesk Variable";
|
|
2965
|
+
if (f.includes("outfit")) return "Outfit Variable";
|
|
2966
|
+
if (f.includes("roboto variable")) return "Roboto Variable";
|
|
2967
|
+
if (f.includes("roboto condensed")) return "Roboto Condensed";
|
|
2968
|
+
if (f === "roboto") return "Roboto Variable";
|
|
2969
|
+
if (f.includes("open sans")) return "Open Sans Variable";
|
|
2970
|
+
if (f.includes("raleway")) return "Raleway Variable";
|
|
2971
|
+
if (f.includes("oswald")) return "Oswald Variable";
|
|
2972
|
+
if (f.includes("playfair display")) return "Playfair Display Variable";
|
|
2973
|
+
if (f.includes("nunito")) return "Nunito Variable";
|
|
2974
|
+
if (f.includes("dancing script")) return "Dancing Script Variable";
|
|
2975
|
+
if (f === "lato") return "Lato";
|
|
2976
|
+
if (f === "anton") return "Anton";
|
|
2977
|
+
if (f === "bebas neue") return "Bebas Neue";
|
|
2978
|
+
if (f === "poppins") return "Poppins";
|
|
2979
|
+
if (f === "permanent marker") return "Permanent Marker";
|
|
2980
|
+
if (f === "bangers") return "Bangers";
|
|
2981
|
+
if (f === "press start 2p") return "Press Start 2P";
|
|
2982
|
+
if (f === "pacifico") return "Pacifico";
|
|
2983
|
+
return fontFamily;
|
|
2984
|
+
}
|
|
2985
|
+
function _buildConfig(effect, text, fontSize, canvasWidth, canvasHeight, time, clipStartTime, clipDuration) {
|
|
2986
|
+
const fill = effect.fills?.[0];
|
|
2987
|
+
const stroke = effect.strokes?.[0];
|
|
2988
|
+
const shadow = effect.shadows?.[0];
|
|
2989
|
+
const bevel = effect.bevel;
|
|
2990
|
+
const panel = effect.panel;
|
|
2991
|
+
const ratio = fontSize / 100;
|
|
2992
|
+
const config = {
|
|
2993
|
+
// Canvas / text
|
|
2994
|
+
width: canvasWidth,
|
|
2995
|
+
height: canvasHeight,
|
|
2996
|
+
canvasWidth,
|
|
2997
|
+
canvasHeight,
|
|
2998
|
+
text,
|
|
2999
|
+
time: time ?? 0,
|
|
3000
|
+
clipStartTime: clipStartTime ?? 0,
|
|
3001
|
+
clipDuration: clipDuration ?? 5,
|
|
3002
|
+
// Font
|
|
3003
|
+
fontFamily: resolveFontFamilyName(effect.font.family),
|
|
3004
|
+
fontWeight: effect.font.weight,
|
|
3005
|
+
fontStyle: effect.font.style,
|
|
3006
|
+
fontSize,
|
|
3007
|
+
letterSpacing: effect.font.letterSpacing,
|
|
3008
|
+
lineHeight: effect.font.lineHeight
|
|
3009
|
+
};
|
|
3010
|
+
if (effect.animation) {
|
|
3011
|
+
config.animation = effect.animation;
|
|
3012
|
+
}
|
|
3013
|
+
if (fill) {
|
|
3014
|
+
if (fill.type !== void 0) config.fillType = fill.type;
|
|
3015
|
+
if (fill.color !== void 0) config.fillColor = fill.color;
|
|
3016
|
+
if (fill.gradient?.angle !== void 0) config.fillGradientAngle = fill.gradient.angle;
|
|
3017
|
+
if (fill.gradient?.stops !== void 0) config.fillGradientStops = fill.gradient.stops;
|
|
3018
|
+
if (fill.patternType !== void 0) config.patternType = fill.patternType;
|
|
3019
|
+
if (fill.perCharFillEnabled !== void 0) config.perCharFillEnabled = fill.perCharFillEnabled;
|
|
3020
|
+
if (fill.charFillColors !== void 0) config.charFillColors = fill.charFillColors;
|
|
3021
|
+
} else {
|
|
3022
|
+
config.fillType = "none";
|
|
3023
|
+
}
|
|
3024
|
+
config.strokeEnabled = !!stroke;
|
|
3025
|
+
if (stroke) {
|
|
3026
|
+
if (stroke.color !== void 0) config.strokeColor = stroke.color;
|
|
3027
|
+
if (stroke.width !== void 0) config.strokeWidth = stroke.width * ratio;
|
|
3028
|
+
if (stroke.position !== void 0) config.strokePosition = stroke.position;
|
|
3029
|
+
if (stroke.opacity !== void 0) config.strokeOpacity = stroke.opacity;
|
|
3030
|
+
if (stroke.lineJoin !== void 0) config.strokeLineJoin = stroke.lineJoin;
|
|
3031
|
+
if (stroke.blur !== void 0) config.strokeBlur = stroke.blur * ratio;
|
|
3032
|
+
if (stroke.type !== void 0) config.strokeType = stroke.type;
|
|
3033
|
+
if (stroke.colorSecondary !== void 0) config.strokeColorSecondary = stroke.colorSecondary;
|
|
3034
|
+
if (stroke.widthSecondary !== void 0) config.strokeWidthSecondary = stroke.widthSecondary * ratio;
|
|
3035
|
+
if (stroke.fadeRange !== void 0) config.strokeFadeRange = stroke.fadeRange;
|
|
3036
|
+
}
|
|
3037
|
+
config.shadowEnabled = !!shadow;
|
|
3038
|
+
if (shadow) {
|
|
3039
|
+
if (shadow.color !== void 0) config.shadowColor = shadow.color;
|
|
3040
|
+
if (shadow.blur !== void 0) config.shadowBlur = shadow.blur * ratio;
|
|
3041
|
+
if (shadow.offsetX !== void 0) config.shadowOffsetX = shadow.offsetX * ratio;
|
|
3042
|
+
if (shadow.offsetY !== void 0) config.shadowOffsetY = shadow.offsetY * ratio;
|
|
3043
|
+
if (shadow.opacity !== void 0) config.shadowOpacity = shadow.opacity;
|
|
3044
|
+
if (shadow.type !== void 0) config.shadowType = shadow.type;
|
|
3045
|
+
}
|
|
3046
|
+
config.bevelEnabled = !!bevel;
|
|
3047
|
+
if (bevel) {
|
|
3048
|
+
if (bevel.depth !== void 0) config.bevelDepth = Math.round(bevel.depth * ratio);
|
|
3049
|
+
if (bevel.highlightColor !== void 0) config.bevelHighlight = bevel.highlightColor;
|
|
3050
|
+
if (bevel.shadowColor !== void 0) config.bevelShadow = bevel.shadowColor;
|
|
3051
|
+
if (bevel.direction !== void 0) config.bevelDirection = bevel.direction;
|
|
3052
|
+
if (bevel.coreColor !== void 0) config.bevelCoreColor = bevel.coreColor;
|
|
3053
|
+
if (bevel.edgeColor !== void 0) config.bevelEdgeColor = bevel.edgeColor;
|
|
3054
|
+
if (bevel.edgeWidth !== void 0) config.bevelEdgeWidth = bevel.edgeWidth * ratio;
|
|
3055
|
+
if (bevel.blur !== void 0) config.bevelBlur = bevel.blur * ratio;
|
|
3056
|
+
if (bevel.blurColor !== void 0) config.bevelBlurColor = bevel.blurColor;
|
|
3057
|
+
if (bevel.perspectiveEnabled !== void 0) config.bevelPerspectiveEnabled = bevel.perspectiveEnabled;
|
|
3058
|
+
if (bevel.vanishingPointX !== void 0) config.bevelVanishingPointX = bevel.vanishingPointX;
|
|
3059
|
+
if (bevel.vanishingPointY !== void 0) config.bevelVanishingPointY = bevel.vanishingPointY;
|
|
3060
|
+
if (bevel.focalLength !== void 0) config.bevelFocalLength = bevel.focalLength;
|
|
3061
|
+
}
|
|
3062
|
+
if (effect.stack) {
|
|
3063
|
+
config.stackEnabled = !!effect.stack.count;
|
|
3064
|
+
if (effect.stack.count !== void 0) config.stackCount = effect.stack.count;
|
|
3065
|
+
if (effect.stack.offsetX !== void 0) config.stackOffsetX = effect.stack.offsetX * ratio;
|
|
3066
|
+
if (effect.stack.offsetY !== void 0) config.stackOffsetY = effect.stack.offsetY * ratio;
|
|
3067
|
+
if (effect.stack.opacityDecay !== void 0) config.stackOpacityDecay = effect.stack.opacityDecay;
|
|
3068
|
+
if (effect.stack.color1 !== void 0) config.stackColor1 = effect.stack.color1;
|
|
3069
|
+
if (effect.stack.color2 !== void 0) config.stackColor2 = effect.stack.color2;
|
|
3070
|
+
if (effect.stack.color3 !== void 0) config.stackColor3 = effect.stack.color3;
|
|
3071
|
+
if (effect.stack.color4 !== void 0) config.stackColor4 = effect.stack.color4;
|
|
3072
|
+
}
|
|
3073
|
+
config.panelEnabled = !!panel;
|
|
3074
|
+
if (panel) {
|
|
3075
|
+
if (panel.color !== void 0) config.panelColor = panel.color;
|
|
3076
|
+
if (panel.opacity !== void 0) config.panelOpacity = panel.opacity;
|
|
3077
|
+
if (panel.radius !== void 0) config.panelRadius = panel.radius;
|
|
3078
|
+
if (panel.paddingX !== void 0) config.panelPaddingX = panel.paddingX * ratio;
|
|
3079
|
+
if (panel.paddingY !== void 0) config.panelPaddingY = panel.paddingY * ratio;
|
|
3080
|
+
if (panel.stroke !== void 0) {
|
|
3081
|
+
config.panelStrokeEnabled = !!panel.stroke;
|
|
3082
|
+
if (panel.stroke.color !== void 0) config.panelStrokeColor = panel.stroke.color;
|
|
3083
|
+
if (panel.stroke.width !== void 0) config.panelStrokeWidth = panel.stroke.width * ratio;
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
if (effect.glows) {
|
|
3087
|
+
config.glowLayers = effect.glows.map((g) => {
|
|
3088
|
+
const mappedGlow = {
|
|
3089
|
+
enabled: true,
|
|
3090
|
+
color: g.color,
|
|
3091
|
+
blur: typeof g.blur === "number" ? g.blur * ratio : g.blur ?? 0,
|
|
3092
|
+
opacity: g.opacity,
|
|
3093
|
+
type: g.type ?? "outer"
|
|
3094
|
+
};
|
|
3095
|
+
if (g.strength !== void 0) mappedGlow.strength = g.strength;
|
|
3096
|
+
if (g.spread !== void 0) mappedGlow.spread = g.spread * ratio;
|
|
3097
|
+
return mappedGlow;
|
|
3098
|
+
});
|
|
3099
|
+
}
|
|
3100
|
+
const standardKeys = /* @__PURE__ */ new Set([
|
|
3101
|
+
"id",
|
|
3102
|
+
"name",
|
|
3103
|
+
"category",
|
|
3104
|
+
"description",
|
|
3105
|
+
"tags",
|
|
3106
|
+
"font",
|
|
3107
|
+
"fills",
|
|
3108
|
+
"strokes",
|
|
3109
|
+
"shadows",
|
|
3110
|
+
"glows",
|
|
3111
|
+
"bevel",
|
|
3112
|
+
"panel",
|
|
3113
|
+
"text",
|
|
3114
|
+
"animation",
|
|
3115
|
+
"stack"
|
|
3116
|
+
]);
|
|
3117
|
+
for (const key of Object.keys(effect)) {
|
|
3118
|
+
if (!standardKeys.has(key)) {
|
|
3119
|
+
config[key] = effect[key];
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
return config;
|
|
3123
|
+
}
|
|
3124
|
+
function layerToTextEffectConfig(layer2) {
|
|
3125
|
+
const normWeight = typeof layer2.fontWeight === "number" ? layer2.fontWeight : layer2.fontWeight === "bold" ? 700 : 400;
|
|
3126
|
+
const config = {
|
|
3127
|
+
...defaultConfig,
|
|
3128
|
+
width: layer2.width,
|
|
3129
|
+
height: layer2.height,
|
|
3130
|
+
canvasWidth: layer2.width,
|
|
3131
|
+
canvasHeight: layer2.height,
|
|
3132
|
+
text: layer2.text,
|
|
3133
|
+
fontFamily: resolveFontFamilyName(layer2.fontFamily),
|
|
3134
|
+
fontWeight: normWeight,
|
|
3135
|
+
fontStyle: layer2.fontStyle || "normal",
|
|
3136
|
+
fontSize: layer2.fontSize,
|
|
3137
|
+
letterSpacing: layer2.letterSpacing ?? 0,
|
|
3138
|
+
lineHeight: layer2.lineHeight ?? 1.2,
|
|
3139
|
+
fillType: layer2.color ? "solid" : "none",
|
|
3140
|
+
fillColor: layer2.color ?? "#FFFFFF",
|
|
3141
|
+
strokeEnabled: !!layer2.stroke,
|
|
3142
|
+
strokeColor: layer2.stroke?.color ?? "#000000",
|
|
3143
|
+
strokeWidth: layer2.stroke?.width ?? 0,
|
|
3144
|
+
strokePosition: "center",
|
|
3145
|
+
strokeOpacity: 100,
|
|
3146
|
+
strokeLineJoin: "round",
|
|
3147
|
+
shadowEnabled: !!layer2.shadow,
|
|
3148
|
+
shadowColor: layer2.shadow?.color ?? "#000000",
|
|
3149
|
+
shadowBlur: layer2.shadow?.blur ?? 0,
|
|
3150
|
+
shadowOffsetX: layer2.shadow?.offsetX ?? 0,
|
|
3151
|
+
shadowOffsetY: layer2.shadow?.offsetY ?? 0,
|
|
3152
|
+
shadowOpacity: 100,
|
|
3153
|
+
shadowType: "drop",
|
|
3154
|
+
panelEnabled: !!layer2.background,
|
|
3155
|
+
panelColor: layer2.background?.color ?? "#1E1E26",
|
|
3156
|
+
panelOpacity: 80,
|
|
3157
|
+
panelRadius: layer2.background?.borderRadius ?? 6,
|
|
3158
|
+
panelPaddingX: layer2.background?.padding ?? 12,
|
|
3159
|
+
panelPaddingY: layer2.background?.padding ?? 12,
|
|
3160
|
+
textPosX: layer2.textAlign || "center",
|
|
3161
|
+
textPosY: layer2.verticalAlign === "middle" ? "middle" : layer2.verticalAlign || "middle"
|
|
3162
|
+
};
|
|
3163
|
+
return config;
|
|
3164
|
+
}
|
|
2722
3165
|
|
|
2723
3166
|
// src/engine/animation.ts
|
|
2724
3167
|
function ease(t, kind = "linear") {
|
|
@@ -3028,47 +3471,23 @@ function evaluateScene(doc, time, ctx, options = {}) {
|
|
|
3028
3471
|
finishFrame();
|
|
3029
3472
|
return;
|
|
3030
3473
|
}
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
return;
|
|
3038
|
-
}
|
|
3039
|
-
renderTextEffectCore(offCtx, cfg);
|
|
3040
|
-
applyMaskReveal(offCtx, animated, w, h);
|
|
3474
|
+
const temp = CanvasDevice.acquire(w, h);
|
|
3475
|
+
const tctx = temp.getContext("2d");
|
|
3476
|
+
if (tctx) {
|
|
3477
|
+
tctx.clearRect(0, 0, w, h);
|
|
3478
|
+
renderTextEffectCore(tctx, cfg);
|
|
3479
|
+
applyMaskReveal(tctx, animated, w, h);
|
|
3041
3480
|
const compositor = options.compositor ?? getCompositor();
|
|
3042
3481
|
if (compositor?.isSupported) {
|
|
3043
|
-
compositor.renderToContext(ctx,
|
|
3044
|
-
|
|
3045
|
-
}
|
|
3046
|
-
ctx.clearRect(0, 0, w, h);
|
|
3047
|
-
ctx.drawImage(off, 0, 0);
|
|
3048
|
-
return;
|
|
3049
|
-
}
|
|
3050
|
-
if (typeof document !== "undefined") {
|
|
3051
|
-
const temp = document.createElement("canvas");
|
|
3052
|
-
temp.width = w;
|
|
3053
|
-
temp.height = h;
|
|
3054
|
-
const tctx = temp.getContext("2d");
|
|
3055
|
-
if (tctx) {
|
|
3056
|
-
renderTextEffectCore(tctx, cfg);
|
|
3057
|
-
applyMaskReveal(tctx, animated, w, h);
|
|
3058
|
-
const compositor = options.compositor ?? getCompositor();
|
|
3059
|
-
if (compositor?.isSupported) {
|
|
3060
|
-
compositor.renderToContext(ctx, temp, comp);
|
|
3061
|
-
temp.width = 0;
|
|
3062
|
-
temp.height = 0;
|
|
3063
|
-
return;
|
|
3064
|
-
}
|
|
3482
|
+
compositor.renderToContext(ctx, temp, comp);
|
|
3483
|
+
} else {
|
|
3065
3484
|
ctx.clearRect(0, 0, w, h);
|
|
3066
3485
|
ctx.drawImage(temp, 0, 0);
|
|
3067
|
-
temp.width = 0;
|
|
3068
|
-
temp.height = 0;
|
|
3069
|
-
return;
|
|
3070
3486
|
}
|
|
3487
|
+
CanvasDevice.release(temp);
|
|
3488
|
+
return;
|
|
3071
3489
|
}
|
|
3490
|
+
CanvasDevice.release(temp);
|
|
3072
3491
|
renderTextEffectCore(ctx, cfg);
|
|
3073
3492
|
finishFrame();
|
|
3074
3493
|
}
|
|
@@ -6035,6 +6454,7 @@ function duplicateTrackAtPlayhead(doc, trackIndex, previewTime) {
|
|
|
6035
6454
|
export {
|
|
6036
6455
|
COMPOSITION_PRESETS,
|
|
6037
6456
|
CUSTOM_ENGINE_IDS,
|
|
6457
|
+
CanvasDevice,
|
|
6038
6458
|
DEFAULT_CANVAS_HEIGHT,
|
|
6039
6459
|
DEFAULT_CANVAS_WIDTH,
|
|
6040
6460
|
DEFAULT_DURATION,
|
|
@@ -6046,6 +6466,7 @@ export {
|
|
|
6046
6466
|
ENTRANCE_PRESETS,
|
|
6047
6467
|
EXIT_PRESETS,
|
|
6048
6468
|
FONT_WEIGHT_OPTIONS,
|
|
6469
|
+
FontLoader,
|
|
6049
6470
|
InkBrushEngine,
|
|
6050
6471
|
LEGACY_RENDERER_MAP,
|
|
6051
6472
|
LOOP_PRESETS,
|
|
@@ -6057,6 +6478,7 @@ export {
|
|
|
6057
6478
|
TextEffectRenderer,
|
|
6058
6479
|
WEBM_EXPORT_MAX_FRAMES,
|
|
6059
6480
|
WebGLCompositor,
|
|
6481
|
+
_buildConfig,
|
|
6060
6482
|
_resetPlatformCache,
|
|
6061
6483
|
addImageLayer,
|
|
6062
6484
|
addKeyframeAtTime,
|
|
@@ -6114,6 +6536,7 @@ export {
|
|
|
6114
6536
|
encodeGif,
|
|
6115
6537
|
ensureDefaultTimeline,
|
|
6116
6538
|
ensureFontInLottie,
|
|
6539
|
+
ensureFontsLoaded,
|
|
6117
6540
|
evaluateConfig,
|
|
6118
6541
|
evaluateScene,
|
|
6119
6542
|
findTrackIndex,
|
|
@@ -6121,6 +6544,7 @@ export {
|
|
|
6121
6544
|
getAnimatableParamDef,
|
|
6122
6545
|
getAnimatableParamsForLayer,
|
|
6123
6546
|
getDefaultText,
|
|
6547
|
+
getFontLoader,
|
|
6124
6548
|
getLayerById,
|
|
6125
6549
|
getPresetScene,
|
|
6126
6550
|
getSceneTime,
|
|
@@ -6139,6 +6563,7 @@ export {
|
|
|
6139
6563
|
injectText,
|
|
6140
6564
|
injectTextStyle,
|
|
6141
6565
|
isWebMExportSupported,
|
|
6566
|
+
layerToTextEffectConfig,
|
|
6142
6567
|
loadLottieFonts,
|
|
6143
6568
|
lottieColorToHex,
|
|
6144
6569
|
lottieJToAlign,
|
|
@@ -6161,10 +6586,12 @@ export {
|
|
|
6161
6586
|
renderPngSequence,
|
|
6162
6587
|
renderSceneWebM,
|
|
6163
6588
|
renderTextEffectCore,
|
|
6589
|
+
resetFontLoader,
|
|
6164
6590
|
resetSceneTime,
|
|
6165
6591
|
resizeCharFillColors,
|
|
6166
6592
|
resolveAnimatedScalar,
|
|
6167
6593
|
resolveCustomEngineId,
|
|
6594
|
+
resolveFontFamilyName,
|
|
6168
6595
|
restoreLetterSpacing,
|
|
6169
6596
|
scanLottieFonts,
|
|
6170
6597
|
scanTextLayers,
|