reframe-video 0.6.14 → 0.6.16
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/bin.js +21 -2
- package/dist/browserEntry.js +38 -7
- package/dist/cli.js +21 -2
- package/dist/diff.js +21 -2
- package/dist/index.js +41 -8
- package/dist/labels.js +21 -2
- package/dist/trace-cli.js +16 -1
- package/dist/types/ir.d.ts +27 -2
- package/guides/edsl-guide.md +10 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -162,6 +162,8 @@ function compileScene(ir) {
|
|
|
162
162
|
initialValues.set(key("camera", "zoom"), cam.zoom ?? 1);
|
|
163
163
|
initialValues.set(key("camera", "rotation"), cam.rotation ?? 0);
|
|
164
164
|
if (cam.perspective !== void 0) initialValues.set(key("camera", "perspective"), cam.perspective);
|
|
165
|
+
if (cam.focus !== void 0) initialValues.set(key("camera", "focus"), cam.focus);
|
|
166
|
+
if (cam.aperture !== void 0) initialValues.set(key("camera", "aperture"), cam.aperture);
|
|
165
167
|
}
|
|
166
168
|
const segments = /* @__PURE__ */ new Map();
|
|
167
169
|
const motionPaths = /* @__PURE__ */ new Map();
|
|
@@ -548,6 +550,8 @@ function validateScene(ir) {
|
|
|
548
550
|
problems.push(`camera.${key2} must be a number`);
|
|
549
551
|
} else if (key2 === "perspective" && value <= 0) {
|
|
550
552
|
problems.push(`camera.perspective must be > 0 (focal distance in px) \u2014 drop it to disable perspective`);
|
|
553
|
+
} else if (key2 === "aperture" && value < 0) {
|
|
554
|
+
problems.push(`camera.aperture must be >= 0 (blur px per unit depth) \u2014 0 disables depth of field`);
|
|
551
555
|
}
|
|
552
556
|
}
|
|
553
557
|
}
|
|
@@ -629,7 +633,7 @@ var init_validate = __esm({
|
|
|
629
633
|
]);
|
|
630
634
|
IMAGE_FITS = /* @__PURE__ */ new Set(["fill", "cover"]);
|
|
631
635
|
COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "scaleX", "scaleY", "skewX", "skewY", "z", "rotateX", "rotateY", "anchor", "fixed", ...FX_PROPS];
|
|
632
|
-
CAMERA_PROPS = ["x", "y", "zoom", "rotation", "perspective"];
|
|
636
|
+
CAMERA_PROPS = ["x", "y", "zoom", "rotation", "perspective", "focus", "aperture"];
|
|
633
637
|
PROPS_BY_TYPE = {
|
|
634
638
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
635
639
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
@@ -1347,6 +1351,17 @@ var init_behaviors = __esm({
|
|
|
1347
1351
|
});
|
|
1348
1352
|
|
|
1349
1353
|
// ../core/src/interpolate.ts
|
|
1354
|
+
function springEase(stiffness, damping, velocity) {
|
|
1355
|
+
const K = 5;
|
|
1356
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
1357
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
1358
|
+
const coef = (K - velocity) / wd;
|
|
1359
|
+
return (u) => {
|
|
1360
|
+
if (u <= 0) return 0;
|
|
1361
|
+
if (u >= 1) return 1;
|
|
1362
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1350
1365
|
function easeOutBounce(u) {
|
|
1351
1366
|
const n1 = 7.5625;
|
|
1352
1367
|
const d1 = 2.75;
|
|
@@ -1390,7 +1405,11 @@ var init_interpolate = __esm({
|
|
|
1390
1405
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
1391
1406
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
1392
1407
|
easeOutBounce,
|
|
1393
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
1408
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
1409
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
1410
|
+
spring: springEase(100, 10, 0),
|
|
1411
|
+
springBouncy: springEase(180, 8, 0),
|
|
1412
|
+
springStiff: springEase(210, 26, 0)
|
|
1394
1413
|
};
|
|
1395
1414
|
EASE_NAMES = Object.keys(EASE_TABLE);
|
|
1396
1415
|
}
|
package/dist/browserEntry.js
CHANGED
|
@@ -159,6 +159,8 @@
|
|
|
159
159
|
initialValues.set(key("camera", "zoom"), cam.zoom ?? 1);
|
|
160
160
|
initialValues.set(key("camera", "rotation"), cam.rotation ?? 0);
|
|
161
161
|
if (cam.perspective !== void 0) initialValues.set(key("camera", "perspective"), cam.perspective);
|
|
162
|
+
if (cam.focus !== void 0) initialValues.set(key("camera", "focus"), cam.focus);
|
|
163
|
+
if (cam.aperture !== void 0) initialValues.set(key("camera", "aperture"), cam.aperture);
|
|
162
164
|
}
|
|
163
165
|
const segments = /* @__PURE__ */ new Map();
|
|
164
166
|
const motionPaths = /* @__PURE__ */ new Map();
|
|
@@ -409,6 +411,17 @@
|
|
|
409
411
|
var BACK_C3 = BACK_C1 + 1;
|
|
410
412
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
411
413
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
414
|
+
function springEase(stiffness, damping, velocity) {
|
|
415
|
+
const K = 5;
|
|
416
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
417
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
418
|
+
const coef = (K - velocity) / wd;
|
|
419
|
+
return (u) => {
|
|
420
|
+
if (u <= 0) return 0;
|
|
421
|
+
if (u >= 1) return 1;
|
|
422
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
423
|
+
};
|
|
424
|
+
}
|
|
412
425
|
function easeOutBounce(u) {
|
|
413
426
|
const n1 = 7.5625;
|
|
414
427
|
const d1 = 2.75;
|
|
@@ -443,7 +456,11 @@
|
|
|
443
456
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
444
457
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
445
458
|
easeOutBounce,
|
|
446
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
459
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
460
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
461
|
+
spring: springEase(100, 10, 0),
|
|
462
|
+
springBouncy: springEase(180, 8, 0),
|
|
463
|
+
springStiff: springEase(210, 26, 0)
|
|
447
464
|
};
|
|
448
465
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
449
466
|
function resolveEase(ease) {
|
|
@@ -453,6 +470,10 @@
|
|
|
453
470
|
if (!fn) throw new Error(`unknown ease "${ease}" \u2014 valid: ${Object.keys(EASE_TABLE).join(", ")}`);
|
|
454
471
|
return fn;
|
|
455
472
|
}
|
|
473
|
+
if ("spring" in ease) {
|
|
474
|
+
const { stiffness = 100, damping = 10, velocity = 0 } = ease.spring;
|
|
475
|
+
return springEase(stiffness, damping, velocity);
|
|
476
|
+
}
|
|
456
477
|
return cubicBezierEase(...ease.cubicBezier);
|
|
457
478
|
}
|
|
458
479
|
function cubicBezierEase(x1, y1, x2, y2) {
|
|
@@ -721,6 +742,14 @@
|
|
|
721
742
|
const dPersp = persp ? num("camera", "perspective", 0) : 0;
|
|
722
743
|
const vx = persp ? compiled2.ir.size.width / 2 : 0;
|
|
723
744
|
const vy = persp ? compiled2.ir.size.height / 2 : 0;
|
|
745
|
+
const aperture = persp ? num("camera", "aperture", 0) : 0;
|
|
746
|
+
const focus = persp ? num("camera", "focus", 0) : 0;
|
|
747
|
+
const dofFx = (fx, depth, project) => {
|
|
748
|
+
if (!project || aperture <= 0) return fx;
|
|
749
|
+
const extra = aperture * Math.abs(depth - focus);
|
|
750
|
+
if (extra <= 0) return fx;
|
|
751
|
+
return { ...fx, blur: z0((fx.blur ?? 0) + extra) };
|
|
752
|
+
};
|
|
724
753
|
const walk = (node, parent, parentOpacity, clips, zAcc, project) => {
|
|
725
754
|
const id = node.id;
|
|
726
755
|
const clipSpread = clips.length > 0 ? { clips } : void 0;
|
|
@@ -743,7 +772,8 @@
|
|
|
743
772
|
y2: y1 + (num(id, "y2", node.props.y2) - y1) * progress,
|
|
744
773
|
stroke: str(id, "stroke", node.props.stroke),
|
|
745
774
|
strokeWidth: num(id, "strokeWidth", node.props.strokeWidth ?? 1),
|
|
746
|
-
|
|
775
|
+
// a line carries no z of its own — DOF uses the inherited subtree depth
|
|
776
|
+
...dofFx(fx, zAcc, project),
|
|
747
777
|
...clipSpread
|
|
748
778
|
});
|
|
749
779
|
return;
|
|
@@ -780,6 +810,7 @@
|
|
|
780
810
|
const tilted = rotX !== 0 || rotY !== 0 ? tiltSkew(m, rotX, rotY, hw, hh, dPersp) : m;
|
|
781
811
|
return projectDepth(tilted, depth, vx, vy, dPersp);
|
|
782
812
|
};
|
|
813
|
+
const leafFx = dofFx(fx, depth, project);
|
|
783
814
|
switch (node.type) {
|
|
784
815
|
case "group": {
|
|
785
816
|
const clipTf = projDraw(matrix, 0, 0);
|
|
@@ -820,7 +851,7 @@
|
|
|
820
851
|
...fill !== void 0 && { fill },
|
|
821
852
|
...stroke !== void 0 && { stroke, strokeWidth },
|
|
822
853
|
...node.type === "rect" && { radius: num(id, "radius", node.props.radius ?? 0) },
|
|
823
|
-
...
|
|
854
|
+
...leafFx,
|
|
824
855
|
...clipSpread
|
|
825
856
|
});
|
|
826
857
|
return;
|
|
@@ -840,7 +871,7 @@
|
|
|
840
871
|
offsetX: -width * ax,
|
|
841
872
|
offsetY: -height * ay,
|
|
842
873
|
...node.props.fit && node.props.fit !== "fill" ? { fit: node.props.fit } : {},
|
|
843
|
-
...
|
|
874
|
+
...leafFx,
|
|
844
875
|
...clipSpread
|
|
845
876
|
});
|
|
846
877
|
return;
|
|
@@ -867,7 +898,7 @@
|
|
|
867
898
|
offsetY: -height * ay,
|
|
868
899
|
frame,
|
|
869
900
|
...node.props.fit && node.props.fit !== "fill" ? { fit: node.props.fit } : {},
|
|
870
|
-
...
|
|
901
|
+
...leafFx,
|
|
871
902
|
...clipSpread
|
|
872
903
|
});
|
|
873
904
|
return;
|
|
@@ -892,7 +923,7 @@
|
|
|
892
923
|
...fill !== void 0 && { fill },
|
|
893
924
|
...stroke !== void 0 && { stroke, strokeWidth: num(id, "strokeWidth", node.props.strokeWidth ?? 1) },
|
|
894
925
|
...needsBox && { bbox: pathBBox(dStr) },
|
|
895
|
-
...
|
|
926
|
+
...leafFx,
|
|
896
927
|
...clipSpread
|
|
897
928
|
});
|
|
898
929
|
return;
|
|
@@ -919,7 +950,7 @@
|
|
|
919
950
|
letterSpacing: num(id, "letterSpacing", node.props.letterSpacing ?? 0),
|
|
920
951
|
align: TEXT_ALIGN[ax] ?? "left",
|
|
921
952
|
baseline: TEXT_BASELINE[ay] ?? "top",
|
|
922
|
-
...
|
|
953
|
+
...leafFx,
|
|
923
954
|
...clipSpread
|
|
924
955
|
});
|
|
925
956
|
return;
|
package/dist/cli.js
CHANGED
|
@@ -149,6 +149,8 @@ function compileScene(ir) {
|
|
|
149
149
|
initialValues.set(key("camera", "zoom"), cam.zoom ?? 1);
|
|
150
150
|
initialValues.set(key("camera", "rotation"), cam.rotation ?? 0);
|
|
151
151
|
if (cam.perspective !== void 0) initialValues.set(key("camera", "perspective"), cam.perspective);
|
|
152
|
+
if (cam.focus !== void 0) initialValues.set(key("camera", "focus"), cam.focus);
|
|
153
|
+
if (cam.aperture !== void 0) initialValues.set(key("camera", "aperture"), cam.aperture);
|
|
152
154
|
}
|
|
153
155
|
const segments = /* @__PURE__ */ new Map();
|
|
154
156
|
const motionPaths = /* @__PURE__ */ new Map();
|
|
@@ -344,7 +346,7 @@ var BLEND_MODES = /* @__PURE__ */ new Set([
|
|
|
344
346
|
]);
|
|
345
347
|
var IMAGE_FITS = /* @__PURE__ */ new Set(["fill", "cover"]);
|
|
346
348
|
var COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "scaleX", "scaleY", "skewX", "skewY", "z", "rotateX", "rotateY", "anchor", "fixed", ...FX_PROPS];
|
|
347
|
-
var CAMERA_PROPS = ["x", "y", "zoom", "rotation", "perspective"];
|
|
349
|
+
var CAMERA_PROPS = ["x", "y", "zoom", "rotation", "perspective", "focus", "aperture"];
|
|
348
350
|
var PROPS_BY_TYPE = {
|
|
349
351
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
350
352
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
@@ -562,6 +564,8 @@ function validateScene(ir) {
|
|
|
562
564
|
problems.push(`camera.${key2} must be a number`);
|
|
563
565
|
} else if (key2 === "perspective" && value <= 0) {
|
|
564
566
|
problems.push(`camera.perspective must be > 0 (focal distance in px) \u2014 drop it to disable perspective`);
|
|
567
|
+
} else if (key2 === "aperture" && value < 0) {
|
|
568
|
+
problems.push(`camera.aperture must be >= 0 (blur px per unit depth) \u2014 0 disables depth of field`);
|
|
565
569
|
}
|
|
566
570
|
}
|
|
567
571
|
}
|
|
@@ -1051,6 +1055,17 @@ var BACK_C2 = BACK_C1 * 1.525;
|
|
|
1051
1055
|
var BACK_C3 = BACK_C1 + 1;
|
|
1052
1056
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
1053
1057
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
1058
|
+
function springEase(stiffness, damping, velocity) {
|
|
1059
|
+
const K = 5;
|
|
1060
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
1061
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
1062
|
+
const coef = (K - velocity) / wd;
|
|
1063
|
+
return (u) => {
|
|
1064
|
+
if (u <= 0) return 0;
|
|
1065
|
+
if (u >= 1) return 1;
|
|
1066
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1054
1069
|
function easeOutBounce(u) {
|
|
1055
1070
|
const n1 = 7.5625;
|
|
1056
1071
|
const d1 = 2.75;
|
|
@@ -1085,7 +1100,11 @@ var EASE_TABLE = {
|
|
|
1085
1100
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
1086
1101
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
1087
1102
|
easeOutBounce,
|
|
1088
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
1103
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
1104
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
1105
|
+
spring: springEase(100, 10, 0),
|
|
1106
|
+
springBouncy: springEase(180, 8, 0),
|
|
1107
|
+
springStiff: springEase(210, 26, 0)
|
|
1089
1108
|
};
|
|
1090
1109
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
1091
1110
|
|
package/dist/diff.js
CHANGED
|
@@ -155,6 +155,8 @@ function compileScene(ir) {
|
|
|
155
155
|
initialValues.set(key("camera", "zoom"), cam.zoom ?? 1);
|
|
156
156
|
initialValues.set(key("camera", "rotation"), cam.rotation ?? 0);
|
|
157
157
|
if (cam.perspective !== void 0) initialValues.set(key("camera", "perspective"), cam.perspective);
|
|
158
|
+
if (cam.focus !== void 0) initialValues.set(key("camera", "focus"), cam.focus);
|
|
159
|
+
if (cam.aperture !== void 0) initialValues.set(key("camera", "aperture"), cam.aperture);
|
|
158
160
|
}
|
|
159
161
|
const segments = /* @__PURE__ */ new Map();
|
|
160
162
|
const motionPaths = /* @__PURE__ */ new Map();
|
|
@@ -350,7 +352,7 @@ var BLEND_MODES = /* @__PURE__ */ new Set([
|
|
|
350
352
|
]);
|
|
351
353
|
var IMAGE_FITS = /* @__PURE__ */ new Set(["fill", "cover"]);
|
|
352
354
|
var COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "scaleX", "scaleY", "skewX", "skewY", "z", "rotateX", "rotateY", "anchor", "fixed", ...FX_PROPS];
|
|
353
|
-
var CAMERA_PROPS = ["x", "y", "zoom", "rotation", "perspective"];
|
|
355
|
+
var CAMERA_PROPS = ["x", "y", "zoom", "rotation", "perspective", "focus", "aperture"];
|
|
354
356
|
var PROPS_BY_TYPE = {
|
|
355
357
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
356
358
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
@@ -568,6 +570,8 @@ function validateScene(ir) {
|
|
|
568
570
|
problems.push(`camera.${key2} must be a number`);
|
|
569
571
|
} else if (key2 === "perspective" && value <= 0) {
|
|
570
572
|
problems.push(`camera.perspective must be > 0 (focal distance in px) \u2014 drop it to disable perspective`);
|
|
573
|
+
} else if (key2 === "aperture" && value < 0) {
|
|
574
|
+
problems.push(`camera.aperture must be >= 0 (blur px per unit depth) \u2014 0 disables depth of field`);
|
|
571
575
|
}
|
|
572
576
|
}
|
|
573
577
|
}
|
|
@@ -640,6 +644,17 @@ var BACK_C2 = BACK_C1 * 1.525;
|
|
|
640
644
|
var BACK_C3 = BACK_C1 + 1;
|
|
641
645
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
642
646
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
647
|
+
function springEase(stiffness, damping, velocity) {
|
|
648
|
+
const K = 5;
|
|
649
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
650
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
651
|
+
const coef = (K - velocity) / wd;
|
|
652
|
+
return (u) => {
|
|
653
|
+
if (u <= 0) return 0;
|
|
654
|
+
if (u >= 1) return 1;
|
|
655
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
656
|
+
};
|
|
657
|
+
}
|
|
643
658
|
function easeOutBounce(u) {
|
|
644
659
|
const n1 = 7.5625;
|
|
645
660
|
const d1 = 2.75;
|
|
@@ -674,7 +689,11 @@ var EASE_TABLE = {
|
|
|
674
689
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
675
690
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
676
691
|
easeOutBounce,
|
|
677
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
692
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
693
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
694
|
+
spring: springEase(100, 10, 0),
|
|
695
|
+
springBouncy: springEase(180, 8, 0),
|
|
696
|
+
springStiff: springEase(210, 26, 0)
|
|
678
697
|
};
|
|
679
698
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
680
699
|
|
package/dist/index.js
CHANGED
|
@@ -159,6 +159,8 @@ function compileScene(ir) {
|
|
|
159
159
|
initialValues.set(key("camera", "zoom"), cam.zoom ?? 1);
|
|
160
160
|
initialValues.set(key("camera", "rotation"), cam.rotation ?? 0);
|
|
161
161
|
if (cam.perspective !== void 0) initialValues.set(key("camera", "perspective"), cam.perspective);
|
|
162
|
+
if (cam.focus !== void 0) initialValues.set(key("camera", "focus"), cam.focus);
|
|
163
|
+
if (cam.aperture !== void 0) initialValues.set(key("camera", "aperture"), cam.aperture);
|
|
162
164
|
}
|
|
163
165
|
const segments = /* @__PURE__ */ new Map();
|
|
164
166
|
const motionPaths = /* @__PURE__ */ new Map();
|
|
@@ -354,7 +356,7 @@ var BLEND_MODES = /* @__PURE__ */ new Set([
|
|
|
354
356
|
]);
|
|
355
357
|
var IMAGE_FITS = /* @__PURE__ */ new Set(["fill", "cover"]);
|
|
356
358
|
var COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "scaleX", "scaleY", "skewX", "skewY", "z", "rotateX", "rotateY", "anchor", "fixed", ...FX_PROPS];
|
|
357
|
-
var CAMERA_PROPS = ["x", "y", "zoom", "rotation", "perspective"];
|
|
359
|
+
var CAMERA_PROPS = ["x", "y", "zoom", "rotation", "perspective", "focus", "aperture"];
|
|
358
360
|
var PROPS_BY_TYPE = {
|
|
359
361
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
360
362
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
@@ -572,6 +574,8 @@ function validateScene(ir) {
|
|
|
572
574
|
problems.push(`camera.${key2} must be a number`);
|
|
573
575
|
} else if (key2 === "perspective" && value <= 0) {
|
|
574
576
|
problems.push(`camera.perspective must be > 0 (focal distance in px) \u2014 drop it to disable perspective`);
|
|
577
|
+
} else if (key2 === "aperture" && value < 0) {
|
|
578
|
+
problems.push(`camera.aperture must be >= 0 (blur px per unit depth) \u2014 0 disables depth of field`);
|
|
575
579
|
}
|
|
576
580
|
}
|
|
577
581
|
}
|
|
@@ -2851,6 +2855,17 @@ var BACK_C2 = BACK_C1 * 1.525;
|
|
|
2851
2855
|
var BACK_C3 = BACK_C1 + 1;
|
|
2852
2856
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
2853
2857
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
2858
|
+
function springEase(stiffness, damping, velocity) {
|
|
2859
|
+
const K3 = 5;
|
|
2860
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
2861
|
+
const wd = K3 / zeta * Math.sqrt(1 - zeta * zeta);
|
|
2862
|
+
const coef = (K3 - velocity) / wd;
|
|
2863
|
+
return (u) => {
|
|
2864
|
+
if (u <= 0) return 0;
|
|
2865
|
+
if (u >= 1) return 1;
|
|
2866
|
+
return 1 - Math.exp(-K3 * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
2867
|
+
};
|
|
2868
|
+
}
|
|
2854
2869
|
function easeOutBounce(u) {
|
|
2855
2870
|
const n1 = 7.5625;
|
|
2856
2871
|
const d1 = 2.75;
|
|
@@ -2885,7 +2900,11 @@ var EASE_TABLE = {
|
|
|
2885
2900
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
2886
2901
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
2887
2902
|
easeOutBounce,
|
|
2888
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
2903
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
2904
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
2905
|
+
spring: springEase(100, 10, 0),
|
|
2906
|
+
springBouncy: springEase(180, 8, 0),
|
|
2907
|
+
springStiff: springEase(210, 26, 0)
|
|
2889
2908
|
};
|
|
2890
2909
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
2891
2910
|
function resolveEase(ease) {
|
|
@@ -2895,6 +2914,10 @@ function resolveEase(ease) {
|
|
|
2895
2914
|
if (!fn) throw new Error(`unknown ease "${ease}" \u2014 valid: ${Object.keys(EASE_TABLE).join(", ")}`);
|
|
2896
2915
|
return fn;
|
|
2897
2916
|
}
|
|
2917
|
+
if ("spring" in ease) {
|
|
2918
|
+
const { stiffness = 100, damping = 10, velocity = 0 } = ease.spring;
|
|
2919
|
+
return springEase(stiffness, damping, velocity);
|
|
2920
|
+
}
|
|
2898
2921
|
return cubicBezierEase(...ease.cubicBezier);
|
|
2899
2922
|
}
|
|
2900
2923
|
function cubicBezierEase(x1, y1, x2, y2) {
|
|
@@ -3195,6 +3218,14 @@ function evaluate(compiled, t) {
|
|
|
3195
3218
|
const dPersp = persp ? num("camera", "perspective", 0) : 0;
|
|
3196
3219
|
const vx = persp ? compiled.ir.size.width / 2 : 0;
|
|
3197
3220
|
const vy = persp ? compiled.ir.size.height / 2 : 0;
|
|
3221
|
+
const aperture = persp ? num("camera", "aperture", 0) : 0;
|
|
3222
|
+
const focus = persp ? num("camera", "focus", 0) : 0;
|
|
3223
|
+
const dofFx = (fx, depth, project) => {
|
|
3224
|
+
if (!project || aperture <= 0) return fx;
|
|
3225
|
+
const extra = aperture * Math.abs(depth - focus);
|
|
3226
|
+
if (extra <= 0) return fx;
|
|
3227
|
+
return { ...fx, blur: z0((fx.blur ?? 0) + extra) };
|
|
3228
|
+
};
|
|
3198
3229
|
const walk = (node, parent, parentOpacity, clips, zAcc, project) => {
|
|
3199
3230
|
const id = node.id;
|
|
3200
3231
|
const clipSpread = clips.length > 0 ? { clips } : void 0;
|
|
@@ -3217,7 +3248,8 @@ function evaluate(compiled, t) {
|
|
|
3217
3248
|
y2: y1 + (num(id, "y2", node.props.y2) - y1) * progress,
|
|
3218
3249
|
stroke: str(id, "stroke", node.props.stroke),
|
|
3219
3250
|
strokeWidth: num(id, "strokeWidth", node.props.strokeWidth ?? 1),
|
|
3220
|
-
|
|
3251
|
+
// a line carries no z of its own — DOF uses the inherited subtree depth
|
|
3252
|
+
...dofFx(fx, zAcc, project),
|
|
3221
3253
|
...clipSpread
|
|
3222
3254
|
});
|
|
3223
3255
|
return;
|
|
@@ -3254,6 +3286,7 @@ function evaluate(compiled, t) {
|
|
|
3254
3286
|
const tilted = rotX !== 0 || rotY !== 0 ? tiltSkew(m, rotX, rotY, hw, hh, dPersp) : m;
|
|
3255
3287
|
return projectDepth(tilted, depth, vx, vy, dPersp);
|
|
3256
3288
|
};
|
|
3289
|
+
const leafFx = dofFx(fx, depth, project);
|
|
3257
3290
|
switch (node.type) {
|
|
3258
3291
|
case "group": {
|
|
3259
3292
|
const clipTf = projDraw(matrix, 0, 0);
|
|
@@ -3294,7 +3327,7 @@ function evaluate(compiled, t) {
|
|
|
3294
3327
|
...fill !== void 0 && { fill },
|
|
3295
3328
|
...stroke !== void 0 && { stroke, strokeWidth },
|
|
3296
3329
|
...node.type === "rect" && { radius: num(id, "radius", node.props.radius ?? 0) },
|
|
3297
|
-
...
|
|
3330
|
+
...leafFx,
|
|
3298
3331
|
...clipSpread
|
|
3299
3332
|
});
|
|
3300
3333
|
return;
|
|
@@ -3314,7 +3347,7 @@ function evaluate(compiled, t) {
|
|
|
3314
3347
|
offsetX: -width * ax,
|
|
3315
3348
|
offsetY: -height * ay,
|
|
3316
3349
|
...node.props.fit && node.props.fit !== "fill" ? { fit: node.props.fit } : {},
|
|
3317
|
-
...
|
|
3350
|
+
...leafFx,
|
|
3318
3351
|
...clipSpread
|
|
3319
3352
|
});
|
|
3320
3353
|
return;
|
|
@@ -3341,7 +3374,7 @@ function evaluate(compiled, t) {
|
|
|
3341
3374
|
offsetY: -height * ay,
|
|
3342
3375
|
frame,
|
|
3343
3376
|
...node.props.fit && node.props.fit !== "fill" ? { fit: node.props.fit } : {},
|
|
3344
|
-
...
|
|
3377
|
+
...leafFx,
|
|
3345
3378
|
...clipSpread
|
|
3346
3379
|
});
|
|
3347
3380
|
return;
|
|
@@ -3366,7 +3399,7 @@ function evaluate(compiled, t) {
|
|
|
3366
3399
|
...fill !== void 0 && { fill },
|
|
3367
3400
|
...stroke !== void 0 && { stroke, strokeWidth: num(id, "strokeWidth", node.props.strokeWidth ?? 1) },
|
|
3368
3401
|
...needsBox && { bbox: pathBBox(dStr) },
|
|
3369
|
-
...
|
|
3402
|
+
...leafFx,
|
|
3370
3403
|
...clipSpread
|
|
3371
3404
|
});
|
|
3372
3405
|
return;
|
|
@@ -3393,7 +3426,7 @@ function evaluate(compiled, t) {
|
|
|
3393
3426
|
letterSpacing: num(id, "letterSpacing", node.props.letterSpacing ?? 0),
|
|
3394
3427
|
align: TEXT_ALIGN[ax] ?? "left",
|
|
3395
3428
|
baseline: TEXT_BASELINE[ay] ?? "top",
|
|
3396
|
-
...
|
|
3429
|
+
...leafFx,
|
|
3397
3430
|
...clipSpread
|
|
3398
3431
|
});
|
|
3399
3432
|
return;
|
package/dist/labels.js
CHANGED
|
@@ -143,6 +143,8 @@ function compileScene(ir) {
|
|
|
143
143
|
initialValues.set(key("camera", "zoom"), cam.zoom ?? 1);
|
|
144
144
|
initialValues.set(key("camera", "rotation"), cam.rotation ?? 0);
|
|
145
145
|
if (cam.perspective !== void 0) initialValues.set(key("camera", "perspective"), cam.perspective);
|
|
146
|
+
if (cam.focus !== void 0) initialValues.set(key("camera", "focus"), cam.focus);
|
|
147
|
+
if (cam.aperture !== void 0) initialValues.set(key("camera", "aperture"), cam.aperture);
|
|
146
148
|
}
|
|
147
149
|
const segments = /* @__PURE__ */ new Map();
|
|
148
150
|
const motionPaths = /* @__PURE__ */ new Map();
|
|
@@ -338,7 +340,7 @@ var BLEND_MODES = /* @__PURE__ */ new Set([
|
|
|
338
340
|
]);
|
|
339
341
|
var IMAGE_FITS = /* @__PURE__ */ new Set(["fill", "cover"]);
|
|
340
342
|
var COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "scaleX", "scaleY", "skewX", "skewY", "z", "rotateX", "rotateY", "anchor", "fixed", ...FX_PROPS];
|
|
341
|
-
var CAMERA_PROPS = ["x", "y", "zoom", "rotation", "perspective"];
|
|
343
|
+
var CAMERA_PROPS = ["x", "y", "zoom", "rotation", "perspective", "focus", "aperture"];
|
|
342
344
|
var PROPS_BY_TYPE = {
|
|
343
345
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
344
346
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
@@ -556,6 +558,8 @@ function validateScene(ir) {
|
|
|
556
558
|
problems.push(`camera.${key2} must be a number`);
|
|
557
559
|
} else if (key2 === "perspective" && value <= 0) {
|
|
558
560
|
problems.push(`camera.perspective must be > 0 (focal distance in px) \u2014 drop it to disable perspective`);
|
|
561
|
+
} else if (key2 === "aperture" && value < 0) {
|
|
562
|
+
problems.push(`camera.aperture must be >= 0 (blur px per unit depth) \u2014 0 disables depth of field`);
|
|
559
563
|
}
|
|
560
564
|
}
|
|
561
565
|
}
|
|
@@ -598,6 +602,17 @@ var BACK_C2 = BACK_C1 * 1.525;
|
|
|
598
602
|
var BACK_C3 = BACK_C1 + 1;
|
|
599
603
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
600
604
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
605
|
+
function springEase(stiffness, damping, velocity) {
|
|
606
|
+
const K = 5;
|
|
607
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
608
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
609
|
+
const coef = (K - velocity) / wd;
|
|
610
|
+
return (u) => {
|
|
611
|
+
if (u <= 0) return 0;
|
|
612
|
+
if (u >= 1) return 1;
|
|
613
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
614
|
+
};
|
|
615
|
+
}
|
|
601
616
|
function easeOutBounce(u) {
|
|
602
617
|
const n1 = 7.5625;
|
|
603
618
|
const d1 = 2.75;
|
|
@@ -632,7 +647,11 @@ var EASE_TABLE = {
|
|
|
632
647
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
633
648
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
634
649
|
easeOutBounce,
|
|
635
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
650
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
651
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
652
|
+
spring: springEase(100, 10, 0),
|
|
653
|
+
springBouncy: springEase(180, 8, 0),
|
|
654
|
+
springStiff: springEase(210, 26, 0)
|
|
636
655
|
};
|
|
637
656
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
638
657
|
|
package/dist/trace-cli.js
CHANGED
|
@@ -42,6 +42,17 @@ var BACK_C2 = BACK_C1 * 1.525;
|
|
|
42
42
|
var BACK_C3 = BACK_C1 + 1;
|
|
43
43
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
44
44
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
45
|
+
function springEase(stiffness, damping, velocity) {
|
|
46
|
+
const K = 5;
|
|
47
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
48
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
49
|
+
const coef = (K - velocity) / wd;
|
|
50
|
+
return (u) => {
|
|
51
|
+
if (u <= 0) return 0;
|
|
52
|
+
if (u >= 1) return 1;
|
|
53
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
54
|
+
};
|
|
55
|
+
}
|
|
45
56
|
function easeOutBounce(u) {
|
|
46
57
|
const n1 = 7.5625;
|
|
47
58
|
const d1 = 2.75;
|
|
@@ -76,7 +87,11 @@ var EASE_TABLE = {
|
|
|
76
87
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
77
88
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
78
89
|
easeOutBounce,
|
|
79
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
90
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
91
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
92
|
+
spring: springEase(100, 10, 0),
|
|
93
|
+
springBouncy: springEase(180, 8, 0),
|
|
94
|
+
springStiff: springEase(210, 26, 0)
|
|
80
95
|
};
|
|
81
96
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
82
97
|
|
package/dist/types/ir.d.ts
CHANGED
|
@@ -8,10 +8,24 @@
|
|
|
8
8
|
* Semantics: a scene is evaluated as a pure function of continuous time
|
|
9
9
|
* `evaluate(scene, tSeconds) -> DisplayList`. `fps` is a render hint only.
|
|
10
10
|
*/
|
|
11
|
-
export type EaseName = "linear" | "easeInQuad" | "easeOutQuad" | "easeInOutQuad" | "easeInCubic" | "easeOutCubic" | "easeInOutCubic" | "easeInQuart" | "easeOutQuart" | "easeInOutQuart" | "easeInExpo" | "easeOutExpo" | "easeInOutExpo" | "easeInBack" | "easeOutBack" | "easeInOutBack" | "easeInElastic" | "easeOutElastic" | "easeInOutElastic" | "easeInBounce" | "easeOutBounce" | "easeInOutBounce";
|
|
11
|
+
export type EaseName = "linear" | "easeInQuad" | "easeOutQuad" | "easeInOutQuad" | "easeInCubic" | "easeOutCubic" | "easeInOutCubic" | "easeInQuart" | "easeOutQuart" | "easeInOutQuart" | "easeInExpo" | "easeOutExpo" | "easeInOutExpo" | "easeInBack" | "easeOutBack" | "easeInOutBack" | "easeInElastic" | "easeOutElastic" | "easeInOutElastic" | "easeInBounce" | "easeOutBounce" | "easeInOutBounce" | "spring" | "springBouncy" | "springStiff";
|
|
12
|
+
/**
|
|
13
|
+
* A custom spring: a damped harmonic oscillator sampled over the tween's normalized
|
|
14
|
+
* 0..1 window (mass = 1). `stiffness`/`damping` set the damping ratio
|
|
15
|
+
* ζ = damping / (2·√stiffness) — the SHAPE knob (low ζ ⇒ bouncy, high ζ ⇒ snappy);
|
|
16
|
+
* `velocity` is an initial launch slope. Defaults: stiffness 100, damping 10
|
|
17
|
+
* (ζ = 0.5), velocity 0.
|
|
18
|
+
*/
|
|
19
|
+
export interface SpringEase {
|
|
20
|
+
spring: {
|
|
21
|
+
stiffness?: number;
|
|
22
|
+
damping?: number;
|
|
23
|
+
velocity?: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
12
26
|
export type Ease = EaseName | {
|
|
13
27
|
cubicBezier: [number, number, number, number];
|
|
14
|
-
};
|
|
28
|
+
} | SpringEase;
|
|
15
29
|
export type Anchor = "top-left" | "top-center" | "top-right" | "center-left" | "center" | "center-right" | "bottom-left" | "bottom-center" | "bottom-right";
|
|
16
30
|
export interface Size {
|
|
17
31
|
width: number;
|
|
@@ -447,6 +461,17 @@ export interface CameraIR {
|
|
|
447
461
|
* focal pull). A node BEHIND the camera (`perspective + z <= 0`) is culled.
|
|
448
462
|
*/
|
|
449
463
|
perspective?: number;
|
|
464
|
+
/**
|
|
465
|
+
* Depth of field (requires `perspective`). `aperture` is the blur strength —
|
|
466
|
+
* screen-pixels of gaussian blur added per unit of depth away from the focal
|
|
467
|
+
* plane; absent / 0 ⇒ no DOF (byte-identical). `focus` is the in-focus depth
|
|
468
|
+
* (same units as a node's world `z`, default 0 = the camera plane). A drawn op
|
|
469
|
+
* at depth `d` gains `aperture · |d − focus|` blur on top of any authored blur,
|
|
470
|
+
* so far (and near) layers soften while the focal plane stays sharp. Both are
|
|
471
|
+
* keyframable — animate `focus` for a rack focus, `aperture` for an iris pull.
|
|
472
|
+
*/
|
|
473
|
+
focus?: number;
|
|
474
|
+
aperture?: number;
|
|
450
475
|
}
|
|
451
476
|
export interface SceneIR {
|
|
452
477
|
version: 1;
|
package/guides/edsl-guide.md
CHANGED
|
@@ -135,7 +135,10 @@ Expressive eases for a premium feel: `easeIn/Out/InOutBack` (overshoots past the
|
|
|
135
135
|
target then settles — a pop/snap), `easeIn/Out/InOutElastic` (rings around the
|
|
136
136
|
target — a playful spring), `easeIn/Out/InOutBounce` (drops and bounces to rest).
|
|
137
137
|
A logo or card "popping" in usually wants `easeOutBack`; a stamp landing,
|
|
138
|
-
`easeOutBounce`.
|
|
138
|
+
`easeOutBounce`. Physical springs settle to rest within the tween's duration:
|
|
139
|
+
`spring` (a natural settle), `springBouncy` (rings more), `springStiff` (snappy,
|
|
140
|
+
barely overshoots) — or tune your own with `{ spring: { stiffness, damping, velocity } }`
|
|
141
|
+
(damping ratio = `damping / (2·√stiffness)`; lower ⇒ bouncier).
|
|
139
142
|
Scene duration is inferred from the timeline. For a **static frame** you can omit
|
|
140
143
|
`timeline` entirely (or set scene `duration: <seconds>`) — a still defaults to a 1s
|
|
141
144
|
render; no throwaway `wait` is needed.
|
|
@@ -212,6 +215,12 @@ scene({
|
|
|
212
215
|
- A node needs a base value to tween (`rotateY: 0` on the card before tweening it to 360).
|
|
213
216
|
- A tilted **group** foreshortens its whole subtree (cos folds into children). Clips project
|
|
214
217
|
by the group's depth. A `fixed` HUD ignores depth (perspective is part of the camera).
|
|
218
|
+
- **Depth of field** (needs `perspective`): add `camera.aperture` (blur px per unit depth) and
|
|
219
|
+
`camera.focus` (the in-focus `z`, default 0). A layer at depth `d` softens by
|
|
220
|
+
`aperture·|d − focus|` while the focal plane stays sharp; keyframe `focus` for a **rack focus**,
|
|
221
|
+
`aperture` for an iris pull. Absent/`0` ⇒ no blur. HUD/UI text should be `fixed` so it stays
|
|
222
|
+
crisp (a `fixed` node opts out of DOF too). It feeds the same `blur` op, so it composes with an
|
|
223
|
+
authored `blur`.
|
|
215
224
|
- **Limits (honest):** `rotateX`/`rotateY` are an affine approximation (cos-foreshorten +
|
|
216
225
|
keystone skew) — a single rotated quad is really a trapezoid Canvas 2D can't draw, so it
|
|
217
226
|
reads as a flip/tilt, not a pixel-true 3D face (that needs WebGL). Depth positioning
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reframe-video",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.16",
|
|
4
4
|
"description": "Declarative motion graphics that AI can write and humans can tweak — human edits survive AI regeneration. Deterministic mp4 renders from a plain-data scene format.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"motion-graphics",
|