@glissade/lottie 0.45.0-pre.0 → 0.45.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.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +86 -2
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1254,7 +1254,15 @@ function emitKeys(keys, fr, toS) {
|
|
|
1254
1254
|
/**
|
|
1255
1255
|
* Sample `tr` at every integer frame across its keyed span (or the whole
|
|
1256
1256
|
* document `[ip, op]` for an expr track with no keys) and emit linear Lottie
|
|
1257
|
-
* keys
|
|
1257
|
+
* keys, then DECIMATE redundant ones ({@link decimateLinearKeys}). `toS` maps a
|
|
1258
|
+
* sampled value to the Lottie `s` payload.
|
|
1259
|
+
*
|
|
1260
|
+
* Dense per-frame sampling is faithful but huge — a spring or named ease over a
|
|
1261
|
+
* long shot densifies to one key per frame on every channel (a real episode
|
|
1262
|
+
* measured ~148k keys / 139 MB). Since Lottie plays LINEAR between keys, most of
|
|
1263
|
+
* those samples lie on the straight run between two others and are redundant;
|
|
1264
|
+
* decimation drops them within a per-component tolerance, collapsing constant and
|
|
1265
|
+
* constant-velocity stretches to their endpoints while keeping the curvature.
|
|
1258
1266
|
*/
|
|
1259
1267
|
function sampleToLottieKeys(tr, fr, ip, op, toS) {
|
|
1260
1268
|
const keys = tr.keys;
|
|
@@ -1283,6 +1291,75 @@ function sampleToLottieKeys(tr, fr, ip, op, toS) {
|
|
|
1283
1291
|
}
|
|
1284
1292
|
out.push(frame);
|
|
1285
1293
|
}
|
|
1294
|
+
return decimateLinearKeys(out);
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Ramer–Douglas–Peucker over linear-interpolated keyframes: keep the endpoints
|
|
1298
|
+
* plus any interior key whose value is NOT reproduced (within `relEps` of each
|
|
1299
|
+
* component's range) by linear interpolation between the kept neighbors — those
|
|
1300
|
+
* are exactly the keys Lottie's linear playback can't recreate, so the rest are
|
|
1301
|
+
* safe to drop. Endpoints (first/last, hence the exact start/end value and time)
|
|
1302
|
+
* are always kept; existing linear handles on the survivors stay valid. Only
|
|
1303
|
+
* flat-numeric `s` payloads are decimated — path `sh` data (nested vertex arrays)
|
|
1304
|
+
* is left dense. `relEps` is a fraction of each channel's value range, so it is
|
|
1305
|
+
* scale-invariant across position (px), opacity (0–100), scale (×100), color (0–1).
|
|
1306
|
+
*/
|
|
1307
|
+
function decimateLinearKeys(keys, relEps = .002) {
|
|
1308
|
+
const n = keys.length;
|
|
1309
|
+
if (n <= 2) return keys;
|
|
1310
|
+
const isFlat = (s) => Array.isArray(s) && s.every((x) => typeof x === "number");
|
|
1311
|
+
if (!keys.every((k) => isFlat(k.s))) return keys;
|
|
1312
|
+
const sAt = (i) => keys[i].s;
|
|
1313
|
+
const dim = sAt(0).length;
|
|
1314
|
+
const min = new Array(dim).fill(Infinity);
|
|
1315
|
+
const max = new Array(dim).fill(-Infinity);
|
|
1316
|
+
for (let i = 0; i < n; i++) {
|
|
1317
|
+
const s = sAt(i);
|
|
1318
|
+
for (let c = 0; c < dim; c++) {
|
|
1319
|
+
const v = s[c];
|
|
1320
|
+
if (v < min[c]) min[c] = v;
|
|
1321
|
+
if (v > max[c]) max[c] = v;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
const invRange = new Array(dim);
|
|
1325
|
+
for (let c = 0; c < dim; c++) {
|
|
1326
|
+
const r = max[c] - min[c];
|
|
1327
|
+
invRange[c] = r > 1e-9 ? 1 / r : 0;
|
|
1328
|
+
}
|
|
1329
|
+
const keep = new Uint8Array(n);
|
|
1330
|
+
keep[0] = 1;
|
|
1331
|
+
keep[n - 1] = 1;
|
|
1332
|
+
const stack = [[0, n - 1]];
|
|
1333
|
+
while (stack.length > 0) {
|
|
1334
|
+
const [lo, hi] = stack.pop();
|
|
1335
|
+
if (hi - lo < 2) continue;
|
|
1336
|
+
const a = sAt(lo);
|
|
1337
|
+
const b = sAt(hi);
|
|
1338
|
+
const ta = keys[lo].t;
|
|
1339
|
+
const span = keys[hi].t - ta;
|
|
1340
|
+
let worst = -1;
|
|
1341
|
+
let worstDev = relEps;
|
|
1342
|
+
for (let i = lo + 1; i < hi; i++) {
|
|
1343
|
+
const s = sAt(i);
|
|
1344
|
+
const f = span > 0 ? (keys[i].t - ta) / span : 0;
|
|
1345
|
+
let dev = 0;
|
|
1346
|
+
for (let c = 0; c < dim; c++) {
|
|
1347
|
+
const chord = a[c] + (b[c] - a[c]) * f;
|
|
1348
|
+
const d = Math.abs(s[c] - chord) * invRange[c];
|
|
1349
|
+
if (d > dev) dev = d;
|
|
1350
|
+
}
|
|
1351
|
+
if (dev > worstDev) {
|
|
1352
|
+
worstDev = dev;
|
|
1353
|
+
worst = i;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
if (worst >= 0) {
|
|
1357
|
+
keep[worst] = 1;
|
|
1358
|
+
stack.push([lo, worst], [worst, hi]);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
const out = [];
|
|
1362
|
+
for (let i = 0; i < n; i++) if (keep[i] === 1) out.push(keys[i]);
|
|
1286
1363
|
return out;
|
|
1287
1364
|
}
|
|
1288
1365
|
//#endregion
|
|
@@ -1346,6 +1423,7 @@ function exportLottie(mod, opts) {
|
|
|
1346
1423
|
};
|
|
1347
1424
|
walkChildren(ctx, scene.root.children, void 0, byNode);
|
|
1348
1425
|
return {
|
|
1426
|
+
v: BODYMOVIN_VERSION,
|
|
1349
1427
|
fr,
|
|
1350
1428
|
ip: 0,
|
|
1351
1429
|
op,
|
|
@@ -1355,6 +1433,12 @@ function exportLottie(mod, opts) {
|
|
|
1355
1433
|
layers: ctx.layers
|
|
1356
1434
|
};
|
|
1357
1435
|
}
|
|
1436
|
+
/**
|
|
1437
|
+
* bodymovin schema version stamped on every export. Strict lottie-web / dotLottie
|
|
1438
|
+
* validators reject a document without a top-level `v`; 5.7.x is a widely-supported
|
|
1439
|
+
* modern version and the shape this exporter emits is a subset of it.
|
|
1440
|
+
*/
|
|
1441
|
+
const BODYMOVIN_VERSION = "5.7.0";
|
|
1358
1442
|
/** Resolve a track target to `[nodeId, propPath]` by the longest registered-id prefix. */
|
|
1359
1443
|
function resolveTrackNode(nodes, target) {
|
|
1360
1444
|
for (let slash = target.lastIndexOf("/"); slash > 0; slash = target.lastIndexOf("/", slash - 1)) {
|
|
@@ -1504,7 +1588,7 @@ function sampleComponentVec(ctx, xt, yt, staticVal, map) {
|
|
|
1504
1588
|
}
|
|
1505
1589
|
out.push(frame);
|
|
1506
1590
|
}
|
|
1507
|
-
return out;
|
|
1591
|
+
return decimateLinearKeys(out);
|
|
1508
1592
|
}
|
|
1509
1593
|
/** Union frame span of a set of tracks (their first→last key), else [ip, op]. */
|
|
1510
1594
|
function frameSpan(ctx, tracks) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glissade/lottie",
|
|
3
|
-
"version": "0.45.0-pre.
|
|
3
|
+
"version": "0.45.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.45.0-pre.
|
|
22
|
-
"@glissade/scene": "0.45.0-pre.
|
|
21
|
+
"@glissade/core": "0.45.0-pre.2",
|
|
22
|
+
"@glissade/scene": "0.45.0-pre.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@glissade/backend-skia": "0.45.0-pre.
|
|
25
|
+
"@glissade/backend-skia": "0.45.0-pre.2"
|
|
26
26
|
},
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|