@clypra/engine 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +426 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +168 -4
- package/dist/index.d.ts +168 -4
- package/dist/index.js +417 -47
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -115,7 +115,7 @@ function layoutWithFontSize(ctx, cfg, fontSize, lines) {
|
|
|
115
115
|
ctx.font = fontStr;
|
|
116
116
|
const lineWidths = lines.map((line) => measureLine(ctx, line, letterSpacing));
|
|
117
117
|
const maxLineWidth = Math.max(...lineWidths, 1);
|
|
118
|
-
const textBlockHeight = fontSize
|
|
118
|
+
const textBlockHeight = lines.length === 1 ? fontSize : (lines.length - 1) * fontSize * lineHeight + fontSize;
|
|
119
119
|
let align = "center";
|
|
120
120
|
let startX = cWidth / 2;
|
|
121
121
|
if (cfg.textPosX === "left") {
|
|
@@ -376,9 +376,8 @@ function createCanvas(width, height) {
|
|
|
376
376
|
}
|
|
377
377
|
function releaseCanvas(canvas) {
|
|
378
378
|
if (canvas instanceof OffscreenCanvas) return;
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
379
|
+
canvas.width = 0;
|
|
380
|
+
canvas.height = 0;
|
|
382
381
|
}
|
|
383
382
|
function _resetPlatformCache() {
|
|
384
383
|
_ctxFilter = null;
|
|
@@ -387,6 +386,46 @@ function _resetPlatformCache() {
|
|
|
387
386
|
_offscreenCanvas = null;
|
|
388
387
|
_webgl2 = null;
|
|
389
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
|
+
};
|
|
390
429
|
|
|
391
430
|
// src/engine/procedural/utils.ts
|
|
392
431
|
function getCanvas2DContext(canvas) {
|
|
@@ -1905,7 +1944,7 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
1905
1944
|
ctx.globalCompositeOperation = "source-atop";
|
|
1906
1945
|
const renderCount = Math.max(1, Math.min(20, layer2.strength ?? 1));
|
|
1907
1946
|
for (let i = 0; i < renderCount; i++) {
|
|
1908
|
-
renderWithShadowTrick("fill", layer2.color, layer2.blur, 0, 0, layer2.opacity, "
|
|
1947
|
+
renderWithShadowTrick("fill", layer2.color, layer2.blur, 0, 0, layer2.opacity, "#000000", layer2.spread ?? 0);
|
|
1909
1948
|
}
|
|
1910
1949
|
ctx.restore();
|
|
1911
1950
|
}
|
|
@@ -1913,7 +1952,7 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
1913
1952
|
if (shadowEnabled && shadowType === "inner" && shadowOpacity > 0) {
|
|
1914
1953
|
ctx.save();
|
|
1915
1954
|
ctx.globalCompositeOperation = "source-atop";
|
|
1916
|
-
renderWithShadowTrick("fill", shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY, shadowOpacity, "
|
|
1955
|
+
renderWithShadowTrick("fill", shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY, shadowOpacity, "#000000");
|
|
1917
1956
|
ctx.restore();
|
|
1918
1957
|
}
|
|
1919
1958
|
if (isGlitch) {
|
|
@@ -2346,12 +2385,148 @@ async function initializeFontSystem() {
|
|
|
2346
2385
|
}
|
|
2347
2386
|
}
|
|
2348
2387
|
function checkFontVariant(variantName) {
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2388
|
+
if (typeof document === "undefined" || !document.fonts) return false;
|
|
2389
|
+
return document.fonts.check(`16px "${variantName}"`);
|
|
2390
|
+
}
|
|
2391
|
+
var FontLoader = class {
|
|
2392
|
+
state = {
|
|
2393
|
+
loading: /* @__PURE__ */ new Set(),
|
|
2394
|
+
loaded: /* @__PURE__ */ new Set(),
|
|
2395
|
+
failed: /* @__PURE__ */ new Map(),
|
|
2396
|
+
promises: /* @__PURE__ */ new Map()
|
|
2397
|
+
};
|
|
2398
|
+
async ensureFont(descriptor) {
|
|
2399
|
+
const key = this.getFontKey(descriptor);
|
|
2400
|
+
if (this.state.loaded.has(key)) {
|
|
2401
|
+
return {
|
|
2402
|
+
font: descriptor,
|
|
2403
|
+
loaded: true,
|
|
2404
|
+
loadTimeMs: 0
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2407
|
+
if (this.state.failed.has(key)) {
|
|
2408
|
+
return {
|
|
2409
|
+
font: descriptor,
|
|
2410
|
+
loaded: false,
|
|
2411
|
+
error: this.state.failed.get(key),
|
|
2412
|
+
loadTimeMs: 0
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2415
|
+
if (this.state.promises.has(key)) {
|
|
2416
|
+
return this.state.promises.get(key);
|
|
2417
|
+
}
|
|
2418
|
+
const promise = this.loadFont(descriptor);
|
|
2419
|
+
this.state.promises.set(key, promise);
|
|
2420
|
+
return promise;
|
|
2421
|
+
}
|
|
2422
|
+
async ensureFonts(descriptors) {
|
|
2423
|
+
return Promise.all(descriptors.map((desc) => this.ensureFont(desc)));
|
|
2424
|
+
}
|
|
2425
|
+
async waitForFontsReady() {
|
|
2426
|
+
if (typeof document === "undefined" || !document.fonts) {
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
await document.fonts.ready;
|
|
2430
|
+
}
|
|
2431
|
+
isLoaded(descriptor) {
|
|
2432
|
+
const key = this.getFontKey(descriptor);
|
|
2433
|
+
return this.state.loaded.has(key);
|
|
2434
|
+
}
|
|
2435
|
+
getStats() {
|
|
2436
|
+
return {
|
|
2437
|
+
loaded: this.state.loaded.size,
|
|
2438
|
+
loading: this.state.loading.size,
|
|
2439
|
+
failed: this.state.failed.size
|
|
2440
|
+
};
|
|
2441
|
+
}
|
|
2442
|
+
clear() {
|
|
2443
|
+
this.state.loading.clear();
|
|
2444
|
+
this.state.loaded.clear();
|
|
2445
|
+
this.state.failed.clear();
|
|
2446
|
+
this.state.promises.clear();
|
|
2447
|
+
}
|
|
2448
|
+
async loadFont(descriptor) {
|
|
2449
|
+
const key = this.getFontKey(descriptor);
|
|
2450
|
+
const startTime = performance.now();
|
|
2451
|
+
this.state.loading.add(key);
|
|
2452
|
+
try {
|
|
2453
|
+
if (typeof document === "undefined" || !document.fonts) {
|
|
2454
|
+
throw new Error("Font API not available");
|
|
2455
|
+
}
|
|
2456
|
+
const weight = this.normalizeFontWeight(descriptor.weight);
|
|
2457
|
+
const style = descriptor.style || "normal";
|
|
2458
|
+
const fontFace = `${style} ${weight} 16px "${descriptor.family}"`;
|
|
2459
|
+
if (document.fonts.check(fontFace)) {
|
|
2460
|
+
this.state.loaded.add(key);
|
|
2461
|
+
this.state.loading.delete(key);
|
|
2462
|
+
this.state.promises.delete(key);
|
|
2463
|
+
return {
|
|
2464
|
+
font: descriptor,
|
|
2465
|
+
loaded: true,
|
|
2466
|
+
loadTimeMs: performance.now() - startTime
|
|
2467
|
+
};
|
|
2468
|
+
}
|
|
2469
|
+
await document.fonts.load(fontFace);
|
|
2470
|
+
if (!document.fonts.check(fontFace)) {
|
|
2471
|
+
throw new Error(`Font "${descriptor.family}" failed to load`);
|
|
2472
|
+
}
|
|
2473
|
+
this.state.loaded.add(key);
|
|
2474
|
+
this.state.loading.delete(key);
|
|
2475
|
+
this.state.promises.delete(key);
|
|
2476
|
+
return {
|
|
2477
|
+
font: descriptor,
|
|
2478
|
+
loaded: true,
|
|
2479
|
+
loadTimeMs: performance.now() - startTime
|
|
2480
|
+
};
|
|
2481
|
+
} catch (error) {
|
|
2482
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2483
|
+
this.state.failed.set(key, errorMessage);
|
|
2484
|
+
this.state.loading.delete(key);
|
|
2485
|
+
this.state.promises.delete(key);
|
|
2486
|
+
return {
|
|
2487
|
+
font: descriptor,
|
|
2488
|
+
loaded: false,
|
|
2489
|
+
error: errorMessage,
|
|
2490
|
+
loadTimeMs: performance.now() - startTime
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
getFontKey(descriptor) {
|
|
2495
|
+
const weight = this.normalizeFontWeight(descriptor.weight);
|
|
2496
|
+
const style = descriptor.style || "normal";
|
|
2497
|
+
return `${descriptor.family}|${weight}|${style}`;
|
|
2498
|
+
}
|
|
2499
|
+
normalizeFontWeight(weight) {
|
|
2500
|
+
if (typeof weight === "number") {
|
|
2501
|
+
return weight;
|
|
2502
|
+
}
|
|
2503
|
+
if (!weight) return 400;
|
|
2504
|
+
const asNum = parseInt(weight, 10);
|
|
2505
|
+
if (!isNaN(asNum) && asNum >= 100 && asNum <= 900) {
|
|
2506
|
+
return asNum;
|
|
2507
|
+
}
|
|
2508
|
+
const weightMap = {
|
|
2509
|
+
normal: 400,
|
|
2510
|
+
bold: 700,
|
|
2511
|
+
lighter: 300,
|
|
2512
|
+
bolder: 700
|
|
2513
|
+
};
|
|
2514
|
+
return weightMap[weight] ?? 400;
|
|
2515
|
+
}
|
|
2516
|
+
};
|
|
2517
|
+
var globalFontLoader = null;
|
|
2518
|
+
function getFontLoader() {
|
|
2519
|
+
if (!globalFontLoader) {
|
|
2520
|
+
globalFontLoader = new FontLoader();
|
|
2521
|
+
}
|
|
2522
|
+
return globalFontLoader;
|
|
2523
|
+
}
|
|
2524
|
+
function resetFontLoader() {
|
|
2525
|
+
globalFontLoader = null;
|
|
2526
|
+
}
|
|
2527
|
+
async function ensureFontsLoaded(descriptors) {
|
|
2528
|
+
const loader = getFontLoader();
|
|
2529
|
+
return loader.ensureFonts(descriptors);
|
|
2355
2530
|
}
|
|
2356
2531
|
|
|
2357
2532
|
// src/engine/timelineDefaults.ts
|
|
@@ -2724,6 +2899,212 @@ function mergeSceneIntoConfig(doc, base) {
|
|
|
2724
2899
|
const out = sceneToConfig({ ...doc, legacyConfig: base });
|
|
2725
2900
|
return out;
|
|
2726
2901
|
}
|
|
2902
|
+
function resolveFontFamilyName(fontFamily) {
|
|
2903
|
+
const f = fontFamily?.toLowerCase() || "";
|
|
2904
|
+
if (f.includes("inter")) return "Inter Variable";
|
|
2905
|
+
if (f.includes("montserrat")) return "Montserrat Variable";
|
|
2906
|
+
if (f.includes("geist")) return "Geist Variable";
|
|
2907
|
+
if (f.includes("space grotesk") || f.includes("grotesk")) return "Space Grotesk Variable";
|
|
2908
|
+
if (f.includes("outfit")) return "Outfit Variable";
|
|
2909
|
+
if (f.includes("roboto variable")) return "Roboto Variable";
|
|
2910
|
+
if (f.includes("roboto condensed")) return "Roboto Condensed";
|
|
2911
|
+
if (f === "roboto") return "Roboto Variable";
|
|
2912
|
+
if (f.includes("open sans")) return "Open Sans Variable";
|
|
2913
|
+
if (f.includes("raleway")) return "Raleway Variable";
|
|
2914
|
+
if (f.includes("oswald")) return "Oswald Variable";
|
|
2915
|
+
if (f.includes("playfair display")) return "Playfair Display Variable";
|
|
2916
|
+
if (f.includes("nunito")) return "Nunito Variable";
|
|
2917
|
+
if (f.includes("dancing script")) return "Dancing Script Variable";
|
|
2918
|
+
if (f === "lato") return "Lato";
|
|
2919
|
+
if (f === "anton") return "Anton";
|
|
2920
|
+
if (f === "bebas neue") return "Bebas Neue";
|
|
2921
|
+
if (f === "poppins") return "Poppins";
|
|
2922
|
+
if (f === "permanent marker") return "Permanent Marker";
|
|
2923
|
+
if (f === "bangers") return "Bangers";
|
|
2924
|
+
if (f === "press start 2p") return "Press Start 2P";
|
|
2925
|
+
if (f === "pacifico") return "Pacifico";
|
|
2926
|
+
return fontFamily;
|
|
2927
|
+
}
|
|
2928
|
+
function _buildConfig(effect, text, fontSize, canvasWidth, canvasHeight, time, clipStartTime, clipDuration) {
|
|
2929
|
+
const fill = effect.fills?.[0];
|
|
2930
|
+
const stroke = effect.strokes?.[0];
|
|
2931
|
+
const shadow = effect.shadows?.[0];
|
|
2932
|
+
const bevel = effect.bevel;
|
|
2933
|
+
const panel = effect.panel;
|
|
2934
|
+
const ratio = fontSize / 100;
|
|
2935
|
+
const config = {
|
|
2936
|
+
// Canvas / text
|
|
2937
|
+
width: canvasWidth,
|
|
2938
|
+
height: canvasHeight,
|
|
2939
|
+
canvasWidth,
|
|
2940
|
+
canvasHeight,
|
|
2941
|
+
text,
|
|
2942
|
+
time: time ?? 0,
|
|
2943
|
+
clipStartTime: clipStartTime ?? 0,
|
|
2944
|
+
clipDuration: clipDuration ?? 5,
|
|
2945
|
+
// Font
|
|
2946
|
+
fontFamily: resolveFontFamilyName(effect.font.family),
|
|
2947
|
+
fontWeight: effect.font.weight,
|
|
2948
|
+
fontStyle: effect.font.style,
|
|
2949
|
+
fontSize,
|
|
2950
|
+
letterSpacing: effect.font.letterSpacing,
|
|
2951
|
+
lineHeight: effect.font.lineHeight
|
|
2952
|
+
};
|
|
2953
|
+
if (effect.animation) {
|
|
2954
|
+
config.animation = effect.animation;
|
|
2955
|
+
}
|
|
2956
|
+
if (fill) {
|
|
2957
|
+
if (fill.type !== void 0) config.fillType = fill.type;
|
|
2958
|
+
if (fill.color !== void 0) config.fillColor = fill.color;
|
|
2959
|
+
if (fill.gradient?.angle !== void 0) config.fillGradientAngle = fill.gradient.angle;
|
|
2960
|
+
if (fill.gradient?.stops !== void 0) config.fillGradientStops = fill.gradient.stops;
|
|
2961
|
+
if (fill.patternType !== void 0) config.patternType = fill.patternType;
|
|
2962
|
+
if (fill.perCharFillEnabled !== void 0) config.perCharFillEnabled = fill.perCharFillEnabled;
|
|
2963
|
+
if (fill.charFillColors !== void 0) config.charFillColors = fill.charFillColors;
|
|
2964
|
+
} else {
|
|
2965
|
+
config.fillType = "none";
|
|
2966
|
+
}
|
|
2967
|
+
config.strokeEnabled = !!stroke;
|
|
2968
|
+
if (stroke) {
|
|
2969
|
+
if (stroke.color !== void 0) config.strokeColor = stroke.color;
|
|
2970
|
+
if (stroke.width !== void 0) config.strokeWidth = stroke.width * ratio;
|
|
2971
|
+
if (stroke.position !== void 0) config.strokePosition = stroke.position;
|
|
2972
|
+
if (stroke.opacity !== void 0) config.strokeOpacity = stroke.opacity;
|
|
2973
|
+
if (stroke.lineJoin !== void 0) config.strokeLineJoin = stroke.lineJoin;
|
|
2974
|
+
if (stroke.blur !== void 0) config.strokeBlur = stroke.blur * ratio;
|
|
2975
|
+
if (stroke.type !== void 0) config.strokeType = stroke.type;
|
|
2976
|
+
if (stroke.colorSecondary !== void 0) config.strokeColorSecondary = stroke.colorSecondary;
|
|
2977
|
+
if (stroke.widthSecondary !== void 0) config.strokeWidthSecondary = stroke.widthSecondary * ratio;
|
|
2978
|
+
if (stroke.fadeRange !== void 0) config.strokeFadeRange = stroke.fadeRange;
|
|
2979
|
+
}
|
|
2980
|
+
config.shadowEnabled = !!shadow;
|
|
2981
|
+
if (shadow) {
|
|
2982
|
+
if (shadow.color !== void 0) config.shadowColor = shadow.color;
|
|
2983
|
+
if (shadow.blur !== void 0) config.shadowBlur = shadow.blur * ratio;
|
|
2984
|
+
if (shadow.offsetX !== void 0) config.shadowOffsetX = shadow.offsetX * ratio;
|
|
2985
|
+
if (shadow.offsetY !== void 0) config.shadowOffsetY = shadow.offsetY * ratio;
|
|
2986
|
+
if (shadow.opacity !== void 0) config.shadowOpacity = shadow.opacity;
|
|
2987
|
+
if (shadow.type !== void 0) config.shadowType = shadow.type;
|
|
2988
|
+
}
|
|
2989
|
+
config.bevelEnabled = !!bevel;
|
|
2990
|
+
if (bevel) {
|
|
2991
|
+
if (bevel.depth !== void 0) config.bevelDepth = Math.round(bevel.depth * ratio);
|
|
2992
|
+
if (bevel.highlightColor !== void 0) config.bevelHighlight = bevel.highlightColor;
|
|
2993
|
+
if (bevel.shadowColor !== void 0) config.bevelShadow = bevel.shadowColor;
|
|
2994
|
+
if (bevel.direction !== void 0) config.bevelDirection = bevel.direction;
|
|
2995
|
+
if (bevel.coreColor !== void 0) config.bevelCoreColor = bevel.coreColor;
|
|
2996
|
+
if (bevel.edgeColor !== void 0) config.bevelEdgeColor = bevel.edgeColor;
|
|
2997
|
+
if (bevel.edgeWidth !== void 0) config.bevelEdgeWidth = bevel.edgeWidth * ratio;
|
|
2998
|
+
if (bevel.blur !== void 0) config.bevelBlur = bevel.blur * ratio;
|
|
2999
|
+
if (bevel.blurColor !== void 0) config.bevelBlurColor = bevel.blurColor;
|
|
3000
|
+
if (bevel.perspectiveEnabled !== void 0) config.bevelPerspectiveEnabled = bevel.perspectiveEnabled;
|
|
3001
|
+
if (bevel.vanishingPointX !== void 0) config.bevelVanishingPointX = bevel.vanishingPointX;
|
|
3002
|
+
if (bevel.vanishingPointY !== void 0) config.bevelVanishingPointY = bevel.vanishingPointY;
|
|
3003
|
+
if (bevel.focalLength !== void 0) config.bevelFocalLength = bevel.focalLength;
|
|
3004
|
+
}
|
|
3005
|
+
if (effect.stack) {
|
|
3006
|
+
config.stackEnabled = !!effect.stack.count;
|
|
3007
|
+
if (effect.stack.count !== void 0) config.stackCount = effect.stack.count;
|
|
3008
|
+
if (effect.stack.offsetX !== void 0) config.stackOffsetX = effect.stack.offsetX * ratio;
|
|
3009
|
+
if (effect.stack.offsetY !== void 0) config.stackOffsetY = effect.stack.offsetY * ratio;
|
|
3010
|
+
if (effect.stack.opacityDecay !== void 0) config.stackOpacityDecay = effect.stack.opacityDecay;
|
|
3011
|
+
if (effect.stack.color1 !== void 0) config.stackColor1 = effect.stack.color1;
|
|
3012
|
+
if (effect.stack.color2 !== void 0) config.stackColor2 = effect.stack.color2;
|
|
3013
|
+
if (effect.stack.color3 !== void 0) config.stackColor3 = effect.stack.color3;
|
|
3014
|
+
if (effect.stack.color4 !== void 0) config.stackColor4 = effect.stack.color4;
|
|
3015
|
+
}
|
|
3016
|
+
config.panelEnabled = !!panel;
|
|
3017
|
+
if (panel) {
|
|
3018
|
+
if (panel.color !== void 0) config.panelColor = panel.color;
|
|
3019
|
+
if (panel.opacity !== void 0) config.panelOpacity = panel.opacity;
|
|
3020
|
+
if (panel.radius !== void 0) config.panelRadius = panel.radius;
|
|
3021
|
+
if (panel.paddingX !== void 0) config.panelPaddingX = panel.paddingX * ratio;
|
|
3022
|
+
if (panel.paddingY !== void 0) config.panelPaddingY = panel.paddingY * ratio;
|
|
3023
|
+
if (panel.stroke !== void 0) {
|
|
3024
|
+
config.panelStrokeEnabled = !!panel.stroke;
|
|
3025
|
+
if (panel.stroke.color !== void 0) config.panelStrokeColor = panel.stroke.color;
|
|
3026
|
+
if (panel.stroke.width !== void 0) config.panelStrokeWidth = panel.stroke.width * ratio;
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
if (effect.glows) {
|
|
3030
|
+
config.glowLayers = effect.glows.map((g) => {
|
|
3031
|
+
const mappedGlow = {
|
|
3032
|
+
enabled: true,
|
|
3033
|
+
color: g.color,
|
|
3034
|
+
blur: typeof g.blur === "number" ? g.blur * ratio : g.blur ?? 0,
|
|
3035
|
+
opacity: g.opacity,
|
|
3036
|
+
type: g.type ?? "outer"
|
|
3037
|
+
};
|
|
3038
|
+
if (g.strength !== void 0) mappedGlow.strength = g.strength;
|
|
3039
|
+
if (g.spread !== void 0) mappedGlow.spread = g.spread * ratio;
|
|
3040
|
+
return mappedGlow;
|
|
3041
|
+
});
|
|
3042
|
+
}
|
|
3043
|
+
const standardKeys = /* @__PURE__ */ new Set([
|
|
3044
|
+
"id",
|
|
3045
|
+
"name",
|
|
3046
|
+
"category",
|
|
3047
|
+
"description",
|
|
3048
|
+
"tags",
|
|
3049
|
+
"font",
|
|
3050
|
+
"fills",
|
|
3051
|
+
"strokes",
|
|
3052
|
+
"shadows",
|
|
3053
|
+
"glows",
|
|
3054
|
+
"bevel",
|
|
3055
|
+
"panel",
|
|
3056
|
+
"text",
|
|
3057
|
+
"animation",
|
|
3058
|
+
"stack"
|
|
3059
|
+
]);
|
|
3060
|
+
for (const key of Object.keys(effect)) {
|
|
3061
|
+
if (!standardKeys.has(key)) {
|
|
3062
|
+
config[key] = effect[key];
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
return config;
|
|
3066
|
+
}
|
|
3067
|
+
function layerToTextEffectConfig(layer2) {
|
|
3068
|
+
const normWeight = typeof layer2.fontWeight === "number" ? layer2.fontWeight : layer2.fontWeight === "bold" ? 700 : 400;
|
|
3069
|
+
const config = {
|
|
3070
|
+
...defaultConfig,
|
|
3071
|
+
width: layer2.width,
|
|
3072
|
+
height: layer2.height,
|
|
3073
|
+
canvasWidth: layer2.width,
|
|
3074
|
+
canvasHeight: layer2.height,
|
|
3075
|
+
text: layer2.text,
|
|
3076
|
+
fontFamily: resolveFontFamilyName(layer2.fontFamily),
|
|
3077
|
+
fontWeight: normWeight,
|
|
3078
|
+
fontStyle: layer2.fontStyle || "normal",
|
|
3079
|
+
fontSize: layer2.fontSize,
|
|
3080
|
+
letterSpacing: layer2.letterSpacing ?? 0,
|
|
3081
|
+
lineHeight: layer2.lineHeight ?? 1.2,
|
|
3082
|
+
fillType: layer2.color ? "solid" : "none",
|
|
3083
|
+
fillColor: layer2.color ?? "#FFFFFF",
|
|
3084
|
+
strokeEnabled: !!layer2.stroke,
|
|
3085
|
+
strokeColor: layer2.stroke?.color ?? "#000000",
|
|
3086
|
+
strokeWidth: layer2.stroke?.width ?? 0,
|
|
3087
|
+
strokePosition: "center",
|
|
3088
|
+
strokeOpacity: 100,
|
|
3089
|
+
strokeLineJoin: "round",
|
|
3090
|
+
shadowEnabled: !!layer2.shadow,
|
|
3091
|
+
shadowColor: layer2.shadow?.color ?? "#000000",
|
|
3092
|
+
shadowBlur: layer2.shadow?.blur ?? 0,
|
|
3093
|
+
shadowOffsetX: layer2.shadow?.offsetX ?? 0,
|
|
3094
|
+
shadowOffsetY: layer2.shadow?.offsetY ?? 0,
|
|
3095
|
+
shadowOpacity: 100,
|
|
3096
|
+
shadowType: "drop",
|
|
3097
|
+
panelEnabled: !!layer2.background,
|
|
3098
|
+
panelColor: layer2.background?.color ?? "#1E1E26",
|
|
3099
|
+
panelOpacity: 80,
|
|
3100
|
+
panelRadius: layer2.background?.borderRadius ?? 6,
|
|
3101
|
+
panelPaddingX: layer2.background?.padding ?? 12,
|
|
3102
|
+
panelPaddingY: layer2.background?.padding ?? 12,
|
|
3103
|
+
textPosX: layer2.textAlign || "center",
|
|
3104
|
+
textPosY: layer2.verticalAlign === "middle" ? "middle" : layer2.verticalAlign || "middle"
|
|
3105
|
+
};
|
|
3106
|
+
return config;
|
|
3107
|
+
}
|
|
2727
3108
|
|
|
2728
3109
|
// src/engine/animation.ts
|
|
2729
3110
|
function ease(t, kind = "linear") {
|
|
@@ -3009,6 +3390,10 @@ function getCompositor() {
|
|
|
3009
3390
|
}
|
|
3010
3391
|
return sharedCompositor;
|
|
3011
3392
|
}
|
|
3393
|
+
function disposeSharedCompositor() {
|
|
3394
|
+
sharedCompositor?.dispose();
|
|
3395
|
+
sharedCompositor = null;
|
|
3396
|
+
}
|
|
3012
3397
|
function evaluateScene(doc, time, ctx, options = {}) {
|
|
3013
3398
|
const animated = applyTimelineAtTime(doc, time);
|
|
3014
3399
|
const cfg = sceneToConfig(animated);
|
|
@@ -3029,47 +3414,23 @@ function evaluateScene(doc, time, ctx, options = {}) {
|
|
|
3029
3414
|
finishFrame();
|
|
3030
3415
|
return;
|
|
3031
3416
|
}
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
return;
|
|
3039
|
-
}
|
|
3040
|
-
renderTextEffectCore(offCtx, cfg);
|
|
3041
|
-
applyMaskReveal(offCtx, animated, w, h);
|
|
3417
|
+
const temp = CanvasDevice.acquire(w, h);
|
|
3418
|
+
const tctx = temp.getContext("2d");
|
|
3419
|
+
if (tctx) {
|
|
3420
|
+
tctx.clearRect(0, 0, w, h);
|
|
3421
|
+
renderTextEffectCore(tctx, cfg);
|
|
3422
|
+
applyMaskReveal(tctx, animated, w, h);
|
|
3042
3423
|
const compositor = options.compositor ?? getCompositor();
|
|
3043
3424
|
if (compositor?.isSupported) {
|
|
3044
|
-
compositor.renderToContext(ctx,
|
|
3045
|
-
|
|
3046
|
-
}
|
|
3047
|
-
ctx.clearRect(0, 0, w, h);
|
|
3048
|
-
ctx.drawImage(off, 0, 0);
|
|
3049
|
-
return;
|
|
3050
|
-
}
|
|
3051
|
-
if (typeof document !== "undefined") {
|
|
3052
|
-
const temp = document.createElement("canvas");
|
|
3053
|
-
temp.width = w;
|
|
3054
|
-
temp.height = h;
|
|
3055
|
-
const tctx = temp.getContext("2d");
|
|
3056
|
-
if (tctx) {
|
|
3057
|
-
renderTextEffectCore(tctx, cfg);
|
|
3058
|
-
applyMaskReveal(tctx, animated, w, h);
|
|
3059
|
-
const compositor = options.compositor ?? getCompositor();
|
|
3060
|
-
if (compositor?.isSupported) {
|
|
3061
|
-
compositor.renderToContext(ctx, temp, comp);
|
|
3062
|
-
temp.width = 0;
|
|
3063
|
-
temp.height = 0;
|
|
3064
|
-
return;
|
|
3065
|
-
}
|
|
3425
|
+
compositor.renderToContext(ctx, temp, comp);
|
|
3426
|
+
} else {
|
|
3066
3427
|
ctx.clearRect(0, 0, w, h);
|
|
3067
3428
|
ctx.drawImage(temp, 0, 0);
|
|
3068
|
-
temp.width = 0;
|
|
3069
|
-
temp.height = 0;
|
|
3070
|
-
return;
|
|
3071
3429
|
}
|
|
3430
|
+
CanvasDevice.release(temp);
|
|
3431
|
+
return;
|
|
3072
3432
|
}
|
|
3433
|
+
CanvasDevice.release(temp);
|
|
3073
3434
|
renderTextEffectCore(ctx, cfg);
|
|
3074
3435
|
finishFrame();
|
|
3075
3436
|
}
|
|
@@ -6036,6 +6397,7 @@ function duplicateTrackAtPlayhead(doc, trackIndex, previewTime) {
|
|
|
6036
6397
|
export {
|
|
6037
6398
|
COMPOSITION_PRESETS,
|
|
6038
6399
|
CUSTOM_ENGINE_IDS,
|
|
6400
|
+
CanvasDevice,
|
|
6039
6401
|
DEFAULT_CANVAS_HEIGHT,
|
|
6040
6402
|
DEFAULT_CANVAS_WIDTH,
|
|
6041
6403
|
DEFAULT_DURATION,
|
|
@@ -6047,6 +6409,7 @@ export {
|
|
|
6047
6409
|
ENTRANCE_PRESETS,
|
|
6048
6410
|
EXIT_PRESETS,
|
|
6049
6411
|
FONT_WEIGHT_OPTIONS,
|
|
6412
|
+
FontLoader,
|
|
6050
6413
|
InkBrushEngine,
|
|
6051
6414
|
LEGACY_RENDERER_MAP,
|
|
6052
6415
|
LOOP_PRESETS,
|
|
@@ -6058,6 +6421,7 @@ export {
|
|
|
6058
6421
|
TextEffectRenderer,
|
|
6059
6422
|
WEBM_EXPORT_MAX_FRAMES,
|
|
6060
6423
|
WebGLCompositor,
|
|
6424
|
+
_buildConfig,
|
|
6061
6425
|
_resetPlatformCache,
|
|
6062
6426
|
addImageLayer,
|
|
6063
6427
|
addKeyframeAtTime,
|
|
@@ -6102,6 +6466,7 @@ export {
|
|
|
6102
6466
|
createPulseOpacityTrack,
|
|
6103
6467
|
defaultConfig,
|
|
6104
6468
|
deleteKeyframe,
|
|
6469
|
+
disposeSharedCompositor,
|
|
6105
6470
|
downloadDotLottie,
|
|
6106
6471
|
downloadLottieJson,
|
|
6107
6472
|
downloadPngSequenceZip,
|
|
@@ -6114,6 +6479,7 @@ export {
|
|
|
6114
6479
|
encodeGif,
|
|
6115
6480
|
ensureDefaultTimeline,
|
|
6116
6481
|
ensureFontInLottie,
|
|
6482
|
+
ensureFontsLoaded,
|
|
6117
6483
|
evaluateConfig,
|
|
6118
6484
|
evaluateScene,
|
|
6119
6485
|
findTrackIndex,
|
|
@@ -6121,6 +6487,7 @@ export {
|
|
|
6121
6487
|
getAnimatableParamDef,
|
|
6122
6488
|
getAnimatableParamsForLayer,
|
|
6123
6489
|
getDefaultText,
|
|
6490
|
+
getFontLoader,
|
|
6124
6491
|
getLayerById,
|
|
6125
6492
|
getPresetScene,
|
|
6126
6493
|
getSceneTime,
|
|
@@ -6139,6 +6506,7 @@ export {
|
|
|
6139
6506
|
injectText,
|
|
6140
6507
|
injectTextStyle,
|
|
6141
6508
|
isWebMExportSupported,
|
|
6509
|
+
layerToTextEffectConfig,
|
|
6142
6510
|
loadLottieFonts,
|
|
6143
6511
|
lottieColorToHex,
|
|
6144
6512
|
lottieJToAlign,
|
|
@@ -6161,10 +6529,12 @@ export {
|
|
|
6161
6529
|
renderPngSequence,
|
|
6162
6530
|
renderSceneWebM,
|
|
6163
6531
|
renderTextEffectCore,
|
|
6532
|
+
resetFontLoader,
|
|
6164
6533
|
resetSceneTime,
|
|
6165
6534
|
resizeCharFillColors,
|
|
6166
6535
|
resolveAnimatedScalar,
|
|
6167
6536
|
resolveCustomEngineId,
|
|
6537
|
+
resolveFontFamilyName,
|
|
6168
6538
|
restoreLetterSpacing,
|
|
6169
6539
|
scanLottieFonts,
|
|
6170
6540
|
scanTextLayers,
|