@glissade/lottie 0.54.0 → 0.55.0-pre.1

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