@glissade/lottie 0.48.0-pre.0 → 0.48.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.js +67 -13
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Circle, Group, ImageNode, Path, Rect, Text, createScene } from "@glissade/scene";
|
|
2
|
-
import { cubicBezier, formatColor, parseColor, sampleTrack, track } from "@glissade/core";
|
|
2
|
+
import { compileTimeline, cubicBezier, formatColor, parseColor, sampleTrack, track } from "@glissade/core";
|
|
3
3
|
import "@glissade/core/expr";
|
|
4
4
|
//#region src/spec.ts
|
|
5
5
|
var LottieImportError = class extends Error {
|
|
@@ -1402,7 +1402,56 @@ function sampleToLottieKeys(tr, fr, ip, op, toS) {
|
|
|
1402
1402
|
}
|
|
1403
1403
|
out.push(frame);
|
|
1404
1404
|
}
|
|
1405
|
-
return decimateLinearKeys(out);
|
|
1405
|
+
return anchorSampledSpan(decimateLinearKeys(out), f0, f1, ip, op, (frame) => toS(sampleTrack(tr, frame / fr)));
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Anchor the document boundaries of a densely-SAMPLED channel whose keyed span
|
|
1409
|
+
* [f0,f1] doesn't reach the document bounds [ip,op]. Lottie extrapolates a channel
|
|
1410
|
+
* by HOLDING its first key backward and its last key forward, so when the span
|
|
1411
|
+
* starts AFTER ip the first EMITTED sample holds backward across the whole leading
|
|
1412
|
+
* dormant run — and for a fade-in whose first key rounds to a frame PAST the fade
|
|
1413
|
+
* start (a fractional key time, `round(t·fr) > t·fr`), that first sample is already
|
|
1414
|
+
* non-zero (e.g. ~9%), so a "hidden" element GHOSTS at ~9% from t=0 instead of 0.
|
|
1415
|
+
* Make the true base explicit:
|
|
1416
|
+
* • f0 > ip → PREPEND a HOLD key at ip carrying `sampleAt(ip)` — the value
|
|
1417
|
+
* `sampleTrack` holds across the dormant run (0 for a dormant-at-0 fade). HELD,
|
|
1418
|
+
* not linearly ramped, so a long dormant window stays at the base value the
|
|
1419
|
+
* whole way instead of sloping up to the first sample.
|
|
1420
|
+
* • f1 < op → APPEND a key at op carrying `sampleAt(op)`. Lottie already holds
|
|
1421
|
+
* the last key forward, but a fractional last-key round-DOWN leaves the true
|
|
1422
|
+
* tail value unsampled (e.g. a fade-out whose final 0 is skipped); this pins it.
|
|
1423
|
+
* `body` is the already-decimated sampled keyframes; the boundary keys sit OUTSIDE
|
|
1424
|
+
* it, so decimation (which assumes pure linear segments) never touches them. A span
|
|
1425
|
+
* that already covers [ip,op] (the common integer-keyed case) returns `body`
|
|
1426
|
+
* unchanged — byte-identical to before this fix.
|
|
1427
|
+
*/
|
|
1428
|
+
function anchorSampledSpan(body, f0, f1, ip, op, sampleAt) {
|
|
1429
|
+
if (body.length === 0 || f0 <= ip && f1 >= op) return body;
|
|
1430
|
+
const out = body.slice();
|
|
1431
|
+
if (f1 < op) {
|
|
1432
|
+
const last = out[out.length - 1];
|
|
1433
|
+
out[out.length - 1] = {
|
|
1434
|
+
...last,
|
|
1435
|
+
o: {
|
|
1436
|
+
x: 0,
|
|
1437
|
+
y: 0
|
|
1438
|
+
},
|
|
1439
|
+
i: {
|
|
1440
|
+
x: 1,
|
|
1441
|
+
y: 1
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
out.push({
|
|
1445
|
+
t: op,
|
|
1446
|
+
s: sampleAt(op)
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
if (f0 > ip) out.unshift({
|
|
1450
|
+
t: ip,
|
|
1451
|
+
s: sampleAt(ip),
|
|
1452
|
+
h: 1
|
|
1453
|
+
});
|
|
1454
|
+
return out;
|
|
1406
1455
|
}
|
|
1407
1456
|
/**
|
|
1408
1457
|
* Ramer–Douglas–Peucker over linear-interpolated keyframes: keep the endpoints
|
|
@@ -1531,8 +1580,9 @@ function exportLottie(mod, opts) {
|
|
|
1531
1580
|
const scene = mod.createScene();
|
|
1532
1581
|
const fr = opts.fps ?? mod.timeline.fps ?? 60;
|
|
1533
1582
|
const warn = opts.onWarn ?? ((m) => console.warn(`gs export: ${m}`));
|
|
1583
|
+
const compiled = compileTimeline(mod.timeline);
|
|
1534
1584
|
const byNode = /* @__PURE__ */ new Map();
|
|
1535
|
-
for (const tr of
|
|
1585
|
+
for (const tr of compiled.tracks.values()) {
|
|
1536
1586
|
const resolved = resolveTrackNode(scene.nodes, tr.target);
|
|
1537
1587
|
if (resolved === void 0) {
|
|
1538
1588
|
warn(`track '${tr.target}' targets no node in the scene — dropped`);
|
|
@@ -1676,14 +1726,17 @@ function combineOpacity(ctx, node, tracks, accum) {
|
|
|
1676
1726
|
k: leafStatic * accum.factor * 100
|
|
1677
1727
|
};
|
|
1678
1728
|
const [f0, f1] = frameSpan(ctx, leafTrack ? [leafTrack, ...accum.tracks] : [...accum.tracks]);
|
|
1679
|
-
const
|
|
1680
|
-
|
|
1681
|
-
const t = f / ctx.fr;
|
|
1729
|
+
const sampleAt = (frame) => {
|
|
1730
|
+
const t = frame / ctx.fr;
|
|
1682
1731
|
let product = (leafTrack ? sampleTrack(leafTrack, t) : leafStatic) * accum.factor;
|
|
1683
1732
|
for (const at of accum.tracks) product *= sampleTrack(at, t);
|
|
1733
|
+
return [product * 100];
|
|
1734
|
+
};
|
|
1735
|
+
const out = [];
|
|
1736
|
+
for (let f = f0; f <= f1; f++) {
|
|
1684
1737
|
const frame = {
|
|
1685
1738
|
t: f,
|
|
1686
|
-
s:
|
|
1739
|
+
s: sampleAt(f)
|
|
1687
1740
|
};
|
|
1688
1741
|
if (f < f1) {
|
|
1689
1742
|
frame.o = {
|
|
@@ -1699,7 +1752,7 @@ function combineOpacity(ctx, node, tracks, accum) {
|
|
|
1699
1752
|
}
|
|
1700
1753
|
return {
|
|
1701
1754
|
a: 1,
|
|
1702
|
-
k: decimateLinearKeys(out)
|
|
1755
|
+
k: anchorSampledSpan(decimateLinearKeys(out), f0, f1, ctx.ip, ctx.op, sampleAt)
|
|
1703
1756
|
};
|
|
1704
1757
|
}
|
|
1705
1758
|
function scalarProp(ctx, tracks, prop, staticVal, map) {
|
|
@@ -1773,14 +1826,15 @@ function positionProp(ctx, tracks, staticPos) {
|
|
|
1773
1826
|
}
|
|
1774
1827
|
function sampleComponentVec(ctx, xt, yt, staticVal, map) {
|
|
1775
1828
|
const [f0, f1] = frameSpan(ctx, [xt, yt]);
|
|
1829
|
+
const sampleAt = (frame) => {
|
|
1830
|
+
const t = frame / ctx.fr;
|
|
1831
|
+
return map([xt ? sampleTrack(xt, t) : staticVal[0], yt ? sampleTrack(yt, t) : staticVal[1]]);
|
|
1832
|
+
};
|
|
1776
1833
|
const out = [];
|
|
1777
1834
|
for (let f = f0; f <= f1; f++) {
|
|
1778
|
-
const t = f / ctx.fr;
|
|
1779
|
-
const x = xt ? sampleTrack(xt, t) : staticVal[0];
|
|
1780
|
-
const y = yt ? sampleTrack(yt, t) : staticVal[1];
|
|
1781
1835
|
const frame = {
|
|
1782
1836
|
t: f,
|
|
1783
|
-
s:
|
|
1837
|
+
s: sampleAt(f)
|
|
1784
1838
|
};
|
|
1785
1839
|
if (f < f1) {
|
|
1786
1840
|
frame.o = {
|
|
@@ -1794,7 +1848,7 @@ function sampleComponentVec(ctx, xt, yt, staticVal, map) {
|
|
|
1794
1848
|
}
|
|
1795
1849
|
out.push(frame);
|
|
1796
1850
|
}
|
|
1797
|
-
return decimateLinearKeys(out);
|
|
1851
|
+
return anchorSampledSpan(decimateLinearKeys(out), f0, f1, ctx.ip, ctx.op, sampleAt);
|
|
1798
1852
|
}
|
|
1799
1853
|
/** Union frame span of a set of tracks (their first→last key), else [ip, op]. */
|
|
1800
1854
|
function frameSpan(ctx, tracks) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glissade/lottie",
|
|
3
|
-
"version": "0.48.0-pre.
|
|
3
|
+
"version": "0.48.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.48.0-pre.
|
|
22
|
-
"@glissade/scene": "0.48.0-pre.
|
|
21
|
+
"@glissade/core": "0.48.0-pre.2",
|
|
22
|
+
"@glissade/scene": "0.48.0-pre.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@glissade/backend-skia": "0.48.0-pre.
|
|
25
|
+
"@glissade/backend-skia": "0.48.0-pre.2"
|
|
26
26
|
},
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|