@clypra/engine 1.0.1 → 1.1.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/README.md +386 -0
- package/dist/index.cjs +227 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -1
- package/dist/index.d.ts +120 -1
- package/dist/index.js +211 -106
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,44 @@
|
|
|
1
|
+
// src/engine/schema.ts
|
|
2
|
+
var SCENE_VERSION = 1;
|
|
3
|
+
var DEFAULT_CANVAS_WIDTH = 800;
|
|
4
|
+
var DEFAULT_CANVAS_HEIGHT = 200;
|
|
5
|
+
var DEFAULT_FONT_SIZE = 80;
|
|
6
|
+
var DEFAULT_FPS = 30;
|
|
7
|
+
var DEFAULT_DURATION = 2;
|
|
8
|
+
var CUSTOM_ENGINE_IDS = ["ink"];
|
|
9
|
+
var LEGACY_RENDERER_MAP = {
|
|
10
|
+
InkBrushEngine: "ink"
|
|
11
|
+
};
|
|
12
|
+
var ENGINE_ID_TO_LEGACY = {
|
|
13
|
+
ink: "InkBrushEngine"
|
|
14
|
+
};
|
|
15
|
+
function createEmptyScene(overrides) {
|
|
16
|
+
return {
|
|
17
|
+
version: SCENE_VERSION,
|
|
18
|
+
effectName: "My Effect",
|
|
19
|
+
canvas: { width: DEFAULT_CANVAS_WIDTH, height: DEFAULT_CANVAS_HEIGHT, background: "transparent" },
|
|
20
|
+
text: {
|
|
21
|
+
content: "CLYPRA",
|
|
22
|
+
fontFamily: "Poppins",
|
|
23
|
+
fontWeight: 700,
|
|
24
|
+
fontStyle: "normal",
|
|
25
|
+
fontSize: DEFAULT_FONT_SIZE,
|
|
26
|
+
letterSpacing: 4,
|
|
27
|
+
lineHeight: 1.2,
|
|
28
|
+
textPosX: "center",
|
|
29
|
+
textPosY: "middle"
|
|
30
|
+
},
|
|
31
|
+
effectLayers: [],
|
|
32
|
+
customEngineId: null,
|
|
33
|
+
compositor: { blur: 0, bloom: 0, bloomThreshold: 0.6 },
|
|
34
|
+
timeline: { duration: DEFAULT_DURATION, fps: DEFAULT_FPS, loop: true, tracks: [] },
|
|
35
|
+
...overrides
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function newLayerId() {
|
|
39
|
+
return `layer-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
1
42
|
// src/engine/textLayout.ts
|
|
2
43
|
var COMPOSITION_PRESETS = [
|
|
3
44
|
{ id: "banner", label: "Banner", width: 800, height: 200, description: "Lower third / title bar" },
|
|
@@ -17,8 +58,8 @@ function measureLine(ctx, line, letterSpacing) {
|
|
|
17
58
|
return w;
|
|
18
59
|
}
|
|
19
60
|
function getSafeRect(cfg) {
|
|
20
|
-
const w = cfg.canvasWidth ||
|
|
21
|
-
const h = cfg.canvasHeight ||
|
|
61
|
+
const w = cfg.canvasWidth || DEFAULT_CANVAS_WIDTH;
|
|
62
|
+
const h = cfg.canvasHeight || DEFAULT_CANVAS_HEIGHT;
|
|
22
63
|
const marginX = cfg.panelEnabled ? (cfg.panelPaddingX ?? 40) + 16 : Math.min(48, w * 0.06);
|
|
23
64
|
const marginY = cfg.panelEnabled ? (cfg.panelPaddingY ?? 20) + 16 : Math.min(40, h * 0.1);
|
|
24
65
|
return {
|
|
@@ -65,8 +106,8 @@ function wrapTextToWidth(ctx, text, maxWidth, letterSpacing) {
|
|
|
65
106
|
return lines.length > 0 ? lines : [""];
|
|
66
107
|
}
|
|
67
108
|
function layoutWithFontSize(ctx, cfg, fontSize, lines) {
|
|
68
|
-
const cWidth = cfg.canvasWidth ||
|
|
69
|
-
const cHeight = cfg.canvasHeight ||
|
|
109
|
+
const cWidth = cfg.canvasWidth || DEFAULT_CANVAS_WIDTH;
|
|
110
|
+
const cHeight = cfg.canvasHeight || DEFAULT_CANVAS_HEIGHT;
|
|
70
111
|
const safe = getSafeRect(cfg);
|
|
71
112
|
const lineHeight = cfg.lineHeight ?? 1.2;
|
|
72
113
|
const letterSpacing = cfg.letterSpacing ?? 0;
|
|
@@ -122,7 +163,7 @@ function measureTextFits(ctx, cfg, fontSize, lines) {
|
|
|
122
163
|
return layout.bounds.maxLineWidth <= safe.width + 1 && layout.bounds.textBlockHeight <= safe.height + 1;
|
|
123
164
|
}
|
|
124
165
|
function computeAutoFitFontSize(ctx, cfg, wrappedLines) {
|
|
125
|
-
const max = Math.min(cfg.fontSize ||
|
|
166
|
+
const max = Math.min(cfg.fontSize || DEFAULT_FONT_SIZE, 200);
|
|
126
167
|
let lo = 12;
|
|
127
168
|
let hi = max;
|
|
128
169
|
let best = 12;
|
|
@@ -242,29 +283,112 @@ function shouldUsePerCharFill(cfg) {
|
|
|
242
283
|
return !!cfg.perCharFillEnabled && cfg.fillType === "solid" && !cfg.customRenderer && (cfg.charFillColors?.length ?? 0) > 0;
|
|
243
284
|
}
|
|
244
285
|
|
|
245
|
-
// src/
|
|
246
|
-
|
|
247
|
-
|
|
286
|
+
// src/platform.ts
|
|
287
|
+
var _ctxFilter = null;
|
|
288
|
+
var _roundRect = null;
|
|
289
|
+
var _letterSpacing = null;
|
|
290
|
+
var _offscreenCanvas = null;
|
|
291
|
+
var _webgl2 = null;
|
|
292
|
+
function probe2d() {
|
|
293
|
+
if (typeof document === "undefined") return null;
|
|
294
|
+
try {
|
|
295
|
+
return document.createElement("canvas").getContext("2d");
|
|
296
|
+
} catch {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function supportsCtxFilter() {
|
|
301
|
+
if (_ctxFilter !== null) return _ctxFilter;
|
|
302
|
+
const ctx = probe2d();
|
|
303
|
+
if (!ctx) {
|
|
304
|
+
_ctxFilter = false;
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
ctx.filter = "blur(4px)";
|
|
309
|
+
_ctxFilter = typeof ctx.filter === "string" && ctx.filter.includes("blur");
|
|
310
|
+
} catch {
|
|
311
|
+
_ctxFilter = false;
|
|
312
|
+
}
|
|
313
|
+
return _ctxFilter;
|
|
314
|
+
}
|
|
315
|
+
function supportsRoundRect() {
|
|
316
|
+
if (_roundRect !== null) return _roundRect;
|
|
317
|
+
const ctx = probe2d();
|
|
318
|
+
_roundRect = !!ctx && typeof ctx.roundRect === "function";
|
|
319
|
+
return _roundRect;
|
|
320
|
+
}
|
|
321
|
+
function supportsLetterSpacing() {
|
|
322
|
+
if (_letterSpacing !== null) return _letterSpacing;
|
|
323
|
+
const ctx = probe2d();
|
|
324
|
+
if (!ctx) {
|
|
325
|
+
_letterSpacing = false;
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
ctx.letterSpacing = "2px";
|
|
330
|
+
_letterSpacing = typeof ctx.letterSpacing === "string" && ctx.letterSpacing === "2px";
|
|
331
|
+
} catch {
|
|
332
|
+
_letterSpacing = false;
|
|
333
|
+
}
|
|
334
|
+
return _letterSpacing;
|
|
335
|
+
}
|
|
336
|
+
function supportsOffscreenCanvas() {
|
|
337
|
+
if (_offscreenCanvas !== null) return _offscreenCanvas;
|
|
338
|
+
_offscreenCanvas = typeof globalThis !== "undefined" && typeof globalThis.OffscreenCanvas === "function";
|
|
339
|
+
return _offscreenCanvas;
|
|
340
|
+
}
|
|
341
|
+
function supportsWebGL2() {
|
|
342
|
+
if (_webgl2 !== null) return _webgl2;
|
|
343
|
+
if (typeof document === "undefined") {
|
|
344
|
+
_webgl2 = false;
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
try {
|
|
248
348
|
const canvas = document.createElement("canvas");
|
|
249
|
-
canvas.width =
|
|
250
|
-
canvas.height =
|
|
251
|
-
|
|
349
|
+
canvas.width = 1;
|
|
350
|
+
canvas.height = 1;
|
|
351
|
+
_webgl2 = !!canvas.getContext("webgl2");
|
|
352
|
+
} catch {
|
|
353
|
+
_webgl2 = false;
|
|
252
354
|
}
|
|
253
|
-
|
|
254
|
-
|
|
355
|
+
return _webgl2;
|
|
356
|
+
}
|
|
357
|
+
function createCanvas(width, height) {
|
|
358
|
+
if (supportsOffscreenCanvas()) {
|
|
359
|
+
return new OffscreenCanvas(width, height);
|
|
255
360
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
361
|
+
if (typeof document !== "undefined") {
|
|
362
|
+
const canvas = document.createElement("canvas");
|
|
363
|
+
canvas.width = width;
|
|
364
|
+
canvas.height = height;
|
|
365
|
+
return canvas;
|
|
259
366
|
}
|
|
367
|
+
const factory = globalThis.__clypraCreateCanvas;
|
|
368
|
+
if (factory) return factory(width, height);
|
|
260
369
|
try {
|
|
261
370
|
const nodeRequire = (0, eval)("require");
|
|
262
371
|
const nodeCanvas = nodeRequire("@napi-rs/canvas");
|
|
263
|
-
return nodeCanvas.createCanvas(
|
|
372
|
+
return nodeCanvas.createCanvas(width, height);
|
|
264
373
|
} catch {
|
|
265
|
-
throw new Error("No canvas implementation
|
|
374
|
+
throw new Error("[clypra/engine] No canvas implementation available in this environment. Set globalThis.__clypraCreateCanvas or install @napi-rs/canvas.");
|
|
266
375
|
}
|
|
267
376
|
}
|
|
377
|
+
function releaseCanvas(canvas) {
|
|
378
|
+
if (canvas instanceof OffscreenCanvas) return;
|
|
379
|
+
if (typeof document !== "undefined" && canvas.parentNode) {
|
|
380
|
+
canvas.parentNode.removeChild(canvas);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function _resetPlatformCache() {
|
|
384
|
+
_ctxFilter = null;
|
|
385
|
+
_roundRect = null;
|
|
386
|
+
_letterSpacing = null;
|
|
387
|
+
_offscreenCanvas = null;
|
|
388
|
+
_webgl2 = null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/engine/procedural/utils.ts
|
|
268
392
|
function getCanvas2DContext(canvas) {
|
|
269
393
|
return canvas.getContext("2d");
|
|
270
394
|
}
|
|
@@ -365,8 +489,8 @@ var InkBrushEngine = class {
|
|
|
365
489
|
panelStrokeEnabled: false,
|
|
366
490
|
panelStrokeColor: "#2A2A38",
|
|
367
491
|
panelStrokeWidth: 2,
|
|
368
|
-
canvasWidth:
|
|
369
|
-
canvasHeight:
|
|
492
|
+
canvasWidth: DEFAULT_CANVAS_WIDTH,
|
|
493
|
+
canvasHeight: DEFAULT_CANVAS_HEIGHT,
|
|
370
494
|
textPosX: "center",
|
|
371
495
|
textPosY: "middle",
|
|
372
496
|
inkColor: "#FFFFFF",
|
|
@@ -621,42 +745,39 @@ var InkBrushEngine = class {
|
|
|
621
745
|
}
|
|
622
746
|
};
|
|
623
747
|
|
|
624
|
-
// src/
|
|
625
|
-
function
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
748
|
+
// src/canvas-utils.ts
|
|
749
|
+
function drawRoundedRect(ctx, x, y, w, h, r) {
|
|
750
|
+
const radius = Math.max(0, Math.min(r, Math.min(w, h) / 2));
|
|
751
|
+
ctx.beginPath();
|
|
752
|
+
if (supportsRoundRect()) {
|
|
753
|
+
ctx.roundRect(x, y, w, h, radius);
|
|
754
|
+
} else {
|
|
755
|
+
ctx.moveTo(x + radius, y);
|
|
756
|
+
ctx.lineTo(x + w - radius, y);
|
|
757
|
+
ctx.quadraticCurveTo(x + w, y, x + w, y + radius);
|
|
758
|
+
ctx.lineTo(x + w, y + h - radius);
|
|
759
|
+
ctx.quadraticCurveTo(x + w, y + h, x + w - radius, y + h);
|
|
760
|
+
ctx.lineTo(x + radius, y + h);
|
|
761
|
+
ctx.quadraticCurveTo(x, y + h, x, y + h - radius);
|
|
762
|
+
ctx.lineTo(x, y + radius);
|
|
763
|
+
ctx.quadraticCurveTo(x, y, x + radius, y);
|
|
638
764
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
throw new Error("No canvas implementation found in this environment.");
|
|
765
|
+
}
|
|
766
|
+
function applyLetterSpacing(ctx, value) {
|
|
767
|
+
const prev = ctx.letterSpacing ?? "0px";
|
|
768
|
+
if (value !== 0) {
|
|
769
|
+
ctx.letterSpacing = `${value}px`;
|
|
645
770
|
}
|
|
771
|
+
return prev;
|
|
772
|
+
}
|
|
773
|
+
function restoreLetterSpacing(ctx, saved) {
|
|
774
|
+
ctx.letterSpacing = saved;
|
|
646
775
|
}
|
|
776
|
+
|
|
777
|
+
// src/renderer.ts
|
|
647
778
|
function getCanvas2DContext2(canvas) {
|
|
648
779
|
return canvas.getContext("2d");
|
|
649
780
|
}
|
|
650
|
-
function hexToRgb2(hex) {
|
|
651
|
-
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
|
652
|
-
const fullHex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
|
|
653
|
-
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(fullHex);
|
|
654
|
-
return result ? {
|
|
655
|
-
r: parseInt(result[1], 16),
|
|
656
|
-
g: parseInt(result[2], 16),
|
|
657
|
-
b: parseInt(result[3], 16)
|
|
658
|
-
} : { r: 255, g: 255, b: 255 };
|
|
659
|
-
}
|
|
660
781
|
function renderTextEffectCore(ctx, cfg) {
|
|
661
782
|
if (cfg.customRenderer === "InkBrushEngine") {
|
|
662
783
|
const engine = new InkBrushEngine(cfg);
|
|
@@ -666,8 +787,8 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
666
787
|
const { text, fontFamily, fontWeight, fontStyle, fontSize, letterSpacing, lineHeight, fillType, fillColor, fillGradientAngle, fillGradientStops, patternType, strokeEnabled, strokeColor, strokeWidth, strokePosition, strokeOpacity, strokeLineJoin, strokeBlur, strokeType, strokeColorSecondary, strokeWidthSecondary, strokeFadeRange, glowLayers, shadowEnabled, shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY, shadowOpacity, shadowType, bevelEnabled, bevelDepth, bevelHighlight, bevelShadow, bevelDirection, bevelCoreColor, bevelEdgeColor, bevelEdgeWidth, bevelBlur, bevelBlurColor, bevelPerspectiveEnabled, bevelVanishingPointX, bevelVanishingPointY, bevelFocalLength, stackEnabled, stackCount, stackOffsetX, stackOffsetY, stackOpacityDecay, stackColor1, stackColor2, stackColor3, stackColor4, panelEnabled, panelColor, panelOpacity, panelRadius, panelPaddingX, panelPaddingY, panelStrokeEnabled, panelStrokeColor, panelStrokeWidth, canvasWidth, canvasHeight, textPosX, textPosY } = cfg;
|
|
667
788
|
ctx.imageSmoothingEnabled = true;
|
|
668
789
|
ctx.lineJoin = strokeLineJoin;
|
|
669
|
-
const cWidth = canvasWidth ||
|
|
670
|
-
const cHeight = canvasHeight ||
|
|
790
|
+
const cWidth = canvasWidth || DEFAULT_CANVAS_WIDTH;
|
|
791
|
+
const cHeight = canvasHeight || DEFAULT_CANVAS_HEIGHT;
|
|
671
792
|
const layout = computeTextLayout(ctx, cfg, {
|
|
672
793
|
wrap: cfg.wrapText !== false,
|
|
673
794
|
autoFit: !!cfg.autoFitText
|
|
@@ -879,7 +1000,7 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
879
1000
|
}
|
|
880
1001
|
}
|
|
881
1002
|
} else {
|
|
882
|
-
ctx
|
|
1003
|
+
drawRoundedRect(ctx, px, py, pw, ph, panelRadius);
|
|
883
1004
|
}
|
|
884
1005
|
ctx.closePath();
|
|
885
1006
|
ctx.fill();
|
|
@@ -942,9 +1063,9 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
942
1063
|
ctx.restore();
|
|
943
1064
|
}
|
|
944
1065
|
if (bevelEnabled && bevelDepth > 0) {
|
|
945
|
-
const shadowRgb =
|
|
946
|
-
const coreRgb =
|
|
947
|
-
const highlightRgb =
|
|
1066
|
+
const shadowRgb = hexToRgb(bevelShadow || "#1A0A00");
|
|
1067
|
+
const coreRgb = hexToRgb(bevelCoreColor || bevelShadow || "#3A1A00");
|
|
1068
|
+
const highlightRgb = hexToRgb(bevelHighlight || "#FFFFFF");
|
|
948
1069
|
const shadeForDepth = (t) => {
|
|
949
1070
|
const eased = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
|
950
1071
|
if (eased <= 0.5) {
|
|
@@ -985,7 +1106,7 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
985
1106
|
const t = 1 - (i - 1) / Math.max(1, bevelDepth - 1);
|
|
986
1107
|
const aoFactor = 0.35 + 0.65 * (1 - (i - 1) / Math.max(1, bevelDepth));
|
|
987
1108
|
const baseColor = shadeForDepth(t);
|
|
988
|
-
const bRgb =
|
|
1109
|
+
const bRgb = hexToRgb(baseColor.startsWith("#") ? baseColor : "#000000");
|
|
989
1110
|
const baseRgbParsed = (() => {
|
|
990
1111
|
const m = baseColor.match(/rgb\((\d+),(\d+),(\d+)\)/);
|
|
991
1112
|
return m ? { r: +m[1], g: +m[2], b: +m[3] } : bRgb;
|
|
@@ -1078,7 +1199,7 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
1078
1199
|
let customStrokeStyle = strokeColor;
|
|
1079
1200
|
if (sFadeRange > 0) {
|
|
1080
1201
|
const grad = ctx.createLinearGradient(0, yMin, 0, yMax);
|
|
1081
|
-
const rgb =
|
|
1202
|
+
const rgb = hexToRgb(strokeColor);
|
|
1082
1203
|
grad.addColorStop(0, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${strokeOpacity / 100})`);
|
|
1083
1204
|
const fadeLimit = Math.min(1, sFadeRange / 100);
|
|
1084
1205
|
grad.addColorStop(fadeLimit, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0)`);
|
|
@@ -1188,7 +1309,7 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
1188
1309
|
} else if (fillType === "pattern") {
|
|
1189
1310
|
const pType = patternType || "chalk";
|
|
1190
1311
|
const patColor = fillColor || "#ffffff";
|
|
1191
|
-
const patCanvas =
|
|
1312
|
+
const patCanvas = createCanvas(128, 128);
|
|
1192
1313
|
if (pType === "carbon") {
|
|
1193
1314
|
patCanvas.width = 8;
|
|
1194
1315
|
patCanvas.height = 8;
|
|
@@ -1622,7 +1743,7 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
1622
1743
|
};
|
|
1623
1744
|
const isInkStyle = cfg.effectName.toLowerCase().includes("ink") || cfg.fontFamily.toLowerCase().includes("brush") || cfg.effectName.toLowerCase().includes("grunge") || cfg.effectName.toLowerCase().includes("scratch") || cfg.fontFamily === "Permanent Marker";
|
|
1624
1745
|
if (isInkStyle) {
|
|
1625
|
-
const tCanvas =
|
|
1746
|
+
const tCanvas = createCanvas(cWidth, cHeight);
|
|
1626
1747
|
tCanvas.width = cWidth;
|
|
1627
1748
|
tCanvas.height = cHeight;
|
|
1628
1749
|
const tCtx = getCanvas2DContext2(tCanvas);
|
|
@@ -1751,7 +1872,7 @@ function renderTextEffectCore(ctx, cfg) {
|
|
|
1751
1872
|
}
|
|
1752
1873
|
tCtx.letterSpacing = originalLSpacing;
|
|
1753
1874
|
ctx.save();
|
|
1754
|
-
const tintCanvas =
|
|
1875
|
+
const tintCanvas = createCanvas(cWidth, cHeight);
|
|
1755
1876
|
tintCanvas.width = cWidth;
|
|
1756
1877
|
tintCanvas.height = cHeight;
|
|
1757
1878
|
const tintCtx = getCanvas2DContext2(tintCanvas);
|
|
@@ -1822,7 +1943,7 @@ var defaultConfig = {
|
|
|
1822
1943
|
fontFamily: "Poppins",
|
|
1823
1944
|
fontWeight: 700,
|
|
1824
1945
|
fontStyle: "normal",
|
|
1825
|
-
fontSize:
|
|
1946
|
+
fontSize: DEFAULT_FONT_SIZE,
|
|
1826
1947
|
letterSpacing: 4,
|
|
1827
1948
|
lineHeight: 1.2,
|
|
1828
1949
|
fillType: "solid",
|
|
@@ -1882,8 +2003,8 @@ var defaultConfig = {
|
|
|
1882
2003
|
panelStrokeEnabled: false,
|
|
1883
2004
|
panelStrokeColor: "#2A2A38",
|
|
1884
2005
|
panelStrokeWidth: 2,
|
|
1885
|
-
canvasWidth:
|
|
1886
|
-
canvasHeight:
|
|
2006
|
+
canvasWidth: DEFAULT_CANVAS_WIDTH,
|
|
2007
|
+
canvasHeight: DEFAULT_CANVAS_HEIGHT,
|
|
1887
2008
|
textPosX: "center",
|
|
1888
2009
|
textPosY: "middle",
|
|
1889
2010
|
wrapText: true,
|
|
@@ -2233,42 +2354,6 @@ function checkFontVariant(variantName) {
|
|
|
2233
2354
|
return metrics.width > 0;
|
|
2234
2355
|
}
|
|
2235
2356
|
|
|
2236
|
-
// src/engine/schema.ts
|
|
2237
|
-
var SCENE_VERSION = 1;
|
|
2238
|
-
var CUSTOM_ENGINE_IDS = ["ink"];
|
|
2239
|
-
var LEGACY_RENDERER_MAP = {
|
|
2240
|
-
InkBrushEngine: "ink"
|
|
2241
|
-
};
|
|
2242
|
-
var ENGINE_ID_TO_LEGACY = {
|
|
2243
|
-
ink: "InkBrushEngine"
|
|
2244
|
-
};
|
|
2245
|
-
function createEmptyScene(overrides) {
|
|
2246
|
-
return {
|
|
2247
|
-
version: SCENE_VERSION,
|
|
2248
|
-
effectName: "My Effect",
|
|
2249
|
-
canvas: { width: 800, height: 200, background: "transparent" },
|
|
2250
|
-
text: {
|
|
2251
|
-
content: "CLYPRA",
|
|
2252
|
-
fontFamily: "Poppins",
|
|
2253
|
-
fontWeight: 700,
|
|
2254
|
-
fontStyle: "normal",
|
|
2255
|
-
fontSize: 80,
|
|
2256
|
-
letterSpacing: 4,
|
|
2257
|
-
lineHeight: 1.2,
|
|
2258
|
-
textPosX: "center",
|
|
2259
|
-
textPosY: "middle"
|
|
2260
|
-
},
|
|
2261
|
-
effectLayers: [],
|
|
2262
|
-
customEngineId: null,
|
|
2263
|
-
compositor: { blur: 0, bloom: 0, bloomThreshold: 0.6 },
|
|
2264
|
-
timeline: { duration: 2, fps: 30, loop: true, tracks: [] },
|
|
2265
|
-
...overrides
|
|
2266
|
-
};
|
|
2267
|
-
}
|
|
2268
|
-
function newLayerId() {
|
|
2269
|
-
return `layer-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
2270
|
-
}
|
|
2271
|
-
|
|
2272
2357
|
// src/engine/timelineDefaults.ts
|
|
2273
2358
|
function ensureDefaultTimeline(doc) {
|
|
2274
2359
|
if (doc.timeline.tracks.length > 0) return doc;
|
|
@@ -2927,8 +3012,8 @@ function getCompositor() {
|
|
|
2927
3012
|
function evaluateScene(doc, time, ctx, options = {}) {
|
|
2928
3013
|
const animated = applyTimelineAtTime(doc, time);
|
|
2929
3014
|
const cfg = sceneToConfig(animated);
|
|
2930
|
-
const w = cfg.canvasWidth ||
|
|
2931
|
-
const h = cfg.canvasHeight ||
|
|
3015
|
+
const w = cfg.canvasWidth || DEFAULT_CANVAS_WIDTH;
|
|
3016
|
+
const h = cfg.canvasHeight || DEFAULT_CANVAS_HEIGHT;
|
|
2932
3017
|
const filterLayers = animated.effectLayers.filter((l) => l.type === "filter" && l.enabled);
|
|
2933
3018
|
const lastFilter = filterLayers[filterLayers.length - 1]?.params;
|
|
2934
3019
|
const comp = {
|
|
@@ -2944,8 +3029,8 @@ function evaluateScene(doc, time, ctx, options = {}) {
|
|
|
2944
3029
|
finishFrame();
|
|
2945
3030
|
return;
|
|
2946
3031
|
}
|
|
2947
|
-
|
|
2948
|
-
|
|
3032
|
+
if (supportsOffscreenCanvas()) {
|
|
3033
|
+
const off = new OffscreenCanvas(w, h);
|
|
2949
3034
|
const offCtx = off.getContext("2d");
|
|
2950
3035
|
if (!offCtx) {
|
|
2951
3036
|
renderTextEffectCore(ctx, cfg);
|
|
@@ -2974,10 +3059,14 @@ function evaluateScene(doc, time, ctx, options = {}) {
|
|
|
2974
3059
|
const compositor = options.compositor ?? getCompositor();
|
|
2975
3060
|
if (compositor?.isSupported) {
|
|
2976
3061
|
compositor.renderToContext(ctx, temp, comp);
|
|
3062
|
+
temp.width = 0;
|
|
3063
|
+
temp.height = 0;
|
|
2977
3064
|
return;
|
|
2978
3065
|
}
|
|
2979
3066
|
ctx.clearRect(0, 0, w, h);
|
|
2980
3067
|
ctx.drawImage(temp, 0, 0);
|
|
3068
|
+
temp.width = 0;
|
|
3069
|
+
temp.height = 0;
|
|
2981
3070
|
return;
|
|
2982
3071
|
}
|
|
2983
3072
|
}
|
|
@@ -2988,9 +3077,9 @@ function evaluateConfig(cfg, time, ctx, options) {
|
|
|
2988
3077
|
evaluateScene(textEffectConfigToScene(cfg), time, ctx, options);
|
|
2989
3078
|
}
|
|
2990
3079
|
function advanceSceneTime(doc, steps) {
|
|
2991
|
-
const dt = 1 / (doc.timeline.fps ||
|
|
3080
|
+
const dt = 1 / (doc.timeline.fps || DEFAULT_FPS);
|
|
2992
3081
|
const next = doc._time ?? 0;
|
|
2993
|
-
const duration = doc.timeline.duration ||
|
|
3082
|
+
const duration = doc.timeline.duration || DEFAULT_DURATION;
|
|
2994
3083
|
let t = next + steps * dt;
|
|
2995
3084
|
if (doc.timeline.loop) {
|
|
2996
3085
|
t = duration > 0 ? t % duration : t;
|
|
@@ -5947,6 +6036,11 @@ function duplicateTrackAtPlayhead(doc, trackIndex, previewTime) {
|
|
|
5947
6036
|
export {
|
|
5948
6037
|
COMPOSITION_PRESETS,
|
|
5949
6038
|
CUSTOM_ENGINE_IDS,
|
|
6039
|
+
DEFAULT_CANVAS_HEIGHT,
|
|
6040
|
+
DEFAULT_CANVAS_WIDTH,
|
|
6041
|
+
DEFAULT_DURATION,
|
|
6042
|
+
DEFAULT_FONT_SIZE,
|
|
6043
|
+
DEFAULT_FPS,
|
|
5950
6044
|
DEFAULT_TEXT_STYLE,
|
|
5951
6045
|
EMPHASIS_PRESETS,
|
|
5952
6046
|
ENGINE_ID_TO_LEGACY,
|
|
@@ -5964,6 +6058,7 @@ export {
|
|
|
5964
6058
|
TextEffectRenderer,
|
|
5965
6059
|
WEBM_EXPORT_MAX_FRAMES,
|
|
5966
6060
|
WebGLCompositor,
|
|
6061
|
+
_resetPlatformCache,
|
|
5967
6062
|
addImageLayer,
|
|
5968
6063
|
addKeyframeAtTime,
|
|
5969
6064
|
addOrUpdateKeyframe,
|
|
@@ -5975,6 +6070,7 @@ export {
|
|
|
5975
6070
|
advanceSceneTime,
|
|
5976
6071
|
alignToLottieJ,
|
|
5977
6072
|
applyFillColorToAll,
|
|
6073
|
+
applyLetterSpacing,
|
|
5978
6074
|
applyMaskReveal,
|
|
5979
6075
|
applyRecipeToScene,
|
|
5980
6076
|
applyStyleToLottie,
|
|
@@ -6000,6 +6096,7 @@ export {
|
|
|
6000
6096
|
computeTextLayout,
|
|
6001
6097
|
countTextGlyphs,
|
|
6002
6098
|
createBlankLottie,
|
|
6099
|
+
createCanvas,
|
|
6003
6100
|
createDefaultRevealTrack,
|
|
6004
6101
|
createEmptyScene,
|
|
6005
6102
|
createPulseOpacityTrack,
|
|
@@ -6010,6 +6107,7 @@ export {
|
|
|
6010
6107
|
downloadPngSequenceZip,
|
|
6011
6108
|
downloadSceneWebM,
|
|
6012
6109
|
drawPerCharText,
|
|
6110
|
+
drawRoundedRect,
|
|
6013
6111
|
duplicateTrackAtPlayhead,
|
|
6014
6112
|
ease,
|
|
6015
6113
|
enableKeyframing,
|
|
@@ -6057,6 +6155,7 @@ export {
|
|
|
6057
6155
|
readLayerScalar,
|
|
6058
6156
|
readStyleFromLottieLayer,
|
|
6059
6157
|
reindexLayers,
|
|
6158
|
+
releaseCanvas,
|
|
6060
6159
|
removeKeyframe,
|
|
6061
6160
|
removeTrack,
|
|
6062
6161
|
renderPngSequence,
|
|
@@ -6066,6 +6165,7 @@ export {
|
|
|
6066
6165
|
resizeCharFillColors,
|
|
6067
6166
|
resolveAnimatedScalar,
|
|
6068
6167
|
resolveCustomEngineId,
|
|
6168
|
+
restoreLetterSpacing,
|
|
6069
6169
|
scanLottieFonts,
|
|
6070
6170
|
scanTextLayers,
|
|
6071
6171
|
sceneToConfig,
|
|
@@ -6075,6 +6175,11 @@ export {
|
|
|
6075
6175
|
shouldUsePerCharFill,
|
|
6076
6176
|
snapshotScene,
|
|
6077
6177
|
sortKeyframes,
|
|
6178
|
+
supportsCtxFilter,
|
|
6179
|
+
supportsLetterSpacing,
|
|
6180
|
+
supportsOffscreenCanvas,
|
|
6181
|
+
supportsRoundRect,
|
|
6182
|
+
supportsWebGL2,
|
|
6078
6183
|
syncCompositorFromScene,
|
|
6079
6184
|
textEffectConfigToScene,
|
|
6080
6185
|
trackId,
|