@glissade/lottie 0.55.0-pre.0 → 0.55.0-pre.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.
Files changed (2) hide show
  1. package/dist/index.js +163 -0
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Circle, Group, ImageNode, Path, Rect, Text, breakLines, createScene, meshRasterSize, rasterizeMesh } from "@glissade/scene";
2
2
  import { compileTimeline, cubicBezier, formatColor, parseColor, sampleTrack, track } from "@glissade/core";
3
+ import { Camera, cameraLayerMatrix, shakenSpec } from "@glissade/scene/motion";
3
4
  import "@glissade/core/expr";
4
5
  //#region src/spec.ts
5
6
  var LottieImportError = class extends Error {
@@ -1729,6 +1730,8 @@ function exportLottie(mod, opts) {
1729
1730
  fr,
1730
1731
  ip: 0,
1731
1732
  op,
1733
+ w: opts.width,
1734
+ h: opts.height,
1732
1735
  warn,
1733
1736
  layers: [],
1734
1737
  ind: 0,
@@ -1783,6 +1786,7 @@ function computeOp(tl, fr) {
1783
1786
  function walkChildren(ctx, children, parentInd, byNode, opacity) {
1784
1787
  for (let i = children.length - 1; i >= 0; i--) {
1785
1788
  const node = children[i];
1789
+ if (shakenSpec(node) !== void 0) ctx.warn(`${describe(node)}: shake() jitter is render-only — NOT exported to Lottie (it is a closed-form jitter, not a keyframe track)`);
1786
1790
  const kind = classify(node);
1787
1791
  if (kind === "drop") {
1788
1792
  ctx.warn(`${describe(node)} is not exportable (MVP: Group / Rect / Circle / Path / Text) — dropped`);
@@ -1790,6 +1794,11 @@ function walkChildren(ctx, children, parentInd, byNode, opacity) {
1790
1794
  }
1791
1795
  const myInd = ++ctx.ind;
1792
1796
  const tracks = (node.id !== void 0 ? byNode.get(node.id) : void 0) ?? EMPTY_TRACKS;
1797
+ if (node instanceof Camera) {
1798
+ ctx.layers.push(buildNullLayer(ctx, node, myInd, parentInd, tracks));
1799
+ buildCameraLayers(ctx, node, myInd, byNode, childOpacity(node, tracks, opacity));
1800
+ continue;
1801
+ }
1793
1802
  ctx.layers.push(kind === "group" ? buildNullLayer(ctx, node, myInd, parentInd, tracks) : kind === "text" ? buildTextLayer(ctx, node, myInd, parentInd, tracks, opacity) : buildShapeLayer(ctx, node, kind, myInd, parentInd, tracks, opacity));
1794
1803
  if (node instanceof Group) walkChildren(ctx, node.children, myInd, byNode, childOpacity(node, tracks, opacity));
1795
1804
  }
@@ -2025,6 +2034,160 @@ function buildNullLayer(ctx, node, ind, parentInd, tracks) {
2025
2034
  ...parentInd !== void 0 ? { parent: parentInd } : {}
2026
2035
  };
2027
2036
  }
2037
+ /**
2038
+ * Decompose a per-layer inverse-camera-pose 2x3 into Lottie ks channels. The pose
2039
+ * is `T(screenCenter)·scale(zoom)·rotate(roll)·T(−effectiveCenter)` — an affine
2040
+ * with UNIFORM scale + rotation + translation, i.e. exactly `fromTRS(p, r, [s,s])`
2041
+ * — so it inverts to `p = [e,f]`, `s = |(a,b)|`, `r = atan2(b,a)` (the anchor stays
2042
+ * [0,0], the camera looks at the world, not a boxed node).
2043
+ */
2044
+ function decomposePose(m) {
2045
+ const scale = Math.hypot(m[0], m[1]);
2046
+ const r = Math.atan2(m[1], m[0]) * 180 / Math.PI;
2047
+ return {
2048
+ p: [m[4], m[5]],
2049
+ s: [scale * 100, scale * 100],
2050
+ r
2051
+ };
2052
+ }
2053
+ /** Static camera ks: one constant matrix → constant p/s/r (no keyframes). */
2054
+ function buildStaticCameraKs(m) {
2055
+ const d = decomposePose(m);
2056
+ return {
2057
+ a: {
2058
+ a: 0,
2059
+ k: [0, 0]
2060
+ },
2061
+ p: {
2062
+ a: 0,
2063
+ k: d.p
2064
+ },
2065
+ s: {
2066
+ a: 0,
2067
+ k: d.s
2068
+ },
2069
+ r: {
2070
+ a: 0,
2071
+ k: d.r
2072
+ },
2073
+ o: {
2074
+ a: 0,
2075
+ k: 100
2076
+ }
2077
+ };
2078
+ }
2079
+ /** Dense-sample one decomposed channel across [f0,f1], then decimate + anchor —
2080
+ * the identical discipline sampleComponentVec / combineOpacity use. */
2081
+ function sampleChannel(ctx, f0, f1, sampleAt) {
2082
+ const out = [];
2083
+ for (let f = f0; f <= f1; f++) {
2084
+ const frame = {
2085
+ t: f,
2086
+ s: sampleAt(f)
2087
+ };
2088
+ if (f < f1) {
2089
+ frame.o = {
2090
+ x: 0,
2091
+ y: 0
2092
+ };
2093
+ frame.i = {
2094
+ x: 1,
2095
+ y: 1
2096
+ };
2097
+ }
2098
+ out.push(frame);
2099
+ }
2100
+ return anchorSampledSpan(decimateLinearKeys(out), f0, f1, ctx.ip, ctx.op, sampleAt);
2101
+ }
2102
+ /** Animated camera ks: sample the pose per-frame across the cam-track span and
2103
+ * decompose into p / s / r keyframe channels (a static [0,0] anchor, o 100). */
2104
+ function buildAnimatedCameraKs(ctx, poseAt, camTracks) {
2105
+ const [f0, f1] = frameSpan(ctx, camTracks);
2106
+ const at = (frame) => decomposePose(poseAt(frame / ctx.fr));
2107
+ return {
2108
+ a: {
2109
+ a: 0,
2110
+ k: [0, 0]
2111
+ },
2112
+ p: {
2113
+ a: 1,
2114
+ k: sampleChannel(ctx, f0, f1, (f) => at(f).p)
2115
+ },
2116
+ s: {
2117
+ a: 1,
2118
+ k: sampleChannel(ctx, f0, f1, (f) => at(f).s)
2119
+ },
2120
+ r: {
2121
+ a: 1,
2122
+ k: sampleChannel(ctx, f0, f1, (f) => [at(f).r])
2123
+ },
2124
+ o: {
2125
+ a: 0,
2126
+ k: 100
2127
+ }
2128
+ };
2129
+ }
2130
+ /**
2131
+ * Emit the Camera's depth layers as pose sub-nulls under the camera null. Each
2132
+ * layer gets its own `cameraLayerMatrix` (depth-scaled pan = parallax) sampled at
2133
+ * the export frame grid — STATIC (no cam/* tracks) → constant ks; ANIMATED → ks
2134
+ * keyframes. The layer's content parents to its sub-null, so the pose composes onto
2135
+ * every descendant exactly as Camera.draw applies it at render. A whole-frame shake
2136
+ * is render-only (a closed-form jitter, not a track) → an honest warn, never a
2137
+ * silent drop.
2138
+ */
2139
+ function buildCameraLayers(ctx, cam, camInd, byNode, opacity) {
2140
+ const camTracks = (cam.id !== void 0 ? byNode.get(cam.id) : void 0) ?? EMPTY_TRACKS;
2141
+ const zoomTr = camTracks.get("zoom");
2142
+ const rollTr = camTracks.get("roll");
2143
+ const centerTr = camTracks.get("center");
2144
+ const centerXTr = camTracks.get("center.x");
2145
+ const centerYTr = camTracks.get("center.y");
2146
+ const poseTracks = [
2147
+ zoomTr,
2148
+ rollTr,
2149
+ centerTr,
2150
+ centerXTr,
2151
+ centerYTr
2152
+ ];
2153
+ const animated = poseTracks.some((t) => t !== void 0);
2154
+ const size = {
2155
+ w: ctx.w,
2156
+ h: ctx.h
2157
+ };
2158
+ if (cam.shakeSpec) ctx.warn(`${describe(cam)}: whole-frame camera shake is render-only — NOT exported to Lottie (it is a closed-form jitter, not a keyframe track)`);
2159
+ const zoomAt = (t) => zoomTr ? sampleTrack(zoomTr, t) : cam.zoom();
2160
+ const rollAt = (t) => rollTr ? sampleTrack(rollTr, t) : cam.roll();
2161
+ const centerAt = (t) => {
2162
+ const base = cam.center();
2163
+ let cx = base[0];
2164
+ let cy = base[1];
2165
+ if (centerTr) {
2166
+ const v = sampleTrack(centerTr, t);
2167
+ cx = v[0];
2168
+ cy = v[1];
2169
+ }
2170
+ if (centerXTr) cx = sampleTrack(centerXTr, t);
2171
+ if (centerYTr) cy = sampleTrack(centerYTr, t);
2172
+ return [cx, cy];
2173
+ };
2174
+ for (let i = cam.layers.length - 1; i >= 0; i--) {
2175
+ const layer = cam.layers[i];
2176
+ const subInd = ++ctx.ind;
2177
+ const poseAt = (t) => cameraLayerMatrix(size, centerAt(t), zoomAt(t), rollAt(t), layer.depth);
2178
+ const ks = animated ? buildAnimatedCameraKs(ctx, poseAt, poseTracks) : buildStaticCameraKs(poseAt(0));
2179
+ ctx.layers.push({
2180
+ ty: 3,
2181
+ nm: `${cam.id ?? "cam"}-layer${i}`,
2182
+ ind: subInd,
2183
+ ip: ctx.ip,
2184
+ op: ctx.op,
2185
+ ks,
2186
+ parent: camInd
2187
+ });
2188
+ walkChildren(ctx, [layer.content], subInd, byNode, opacity);
2189
+ }
2190
+ }
2028
2191
  function buildShapeLayer(ctx, node, kind, ind, parentInd, tracks, opacity) {
2029
2192
  const shapes = [buildGeometry(ctx, node, kind, tracks)];
2030
2193
  const stroke = buildStroke(ctx, node, tracks);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/lottie",
3
- "version": "0.55.0-pre.0",
3
+ "version": "0.55.0-pre.2",
4
4
  "description": "glissade Lottie import (S1 MVP): pure .json (Lottie/bodymovin) → node specs + a v1 Timeline. Fail-fast feature audit; no DOM/Node dependencies.",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
@@ -18,11 +18,11 @@
18
18
  "dist"
19
19
  ],
20
20
  "dependencies": {
21
- "@glissade/core": "0.55.0-pre.0",
22
- "@glissade/scene": "0.55.0-pre.0"
21
+ "@glissade/core": "0.55.0-pre.2",
22
+ "@glissade/scene": "0.55.0-pre.2"
23
23
  },
24
24
  "devDependencies": {
25
- "@glissade/backend-skia": "0.55.0-pre.0"
25
+ "@glissade/backend-skia": "0.55.0-pre.2"
26
26
  },
27
27
  "repository": {
28
28
  "type": "git",