@glissade/lottie 0.55.0-pre.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.
- package/dist/index.js +162 -0
- 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.55.0-pre.
|
|
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.55.0-pre.
|
|
22
|
-
"@glissade/scene": "0.55.0-pre.
|
|
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.55.0-pre.
|
|
25
|
+
"@glissade/backend-skia": "0.55.0-pre.1"
|
|
26
26
|
},
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|