diff --git a/pilot/arc.tpl.html b/pilot/arc.tpl.html index 23a78f9..130a807 100644 --- a/pilot/arc.tpl.html +++ b/pilot/arc.tpl.html @@ -943,13 +943,30 @@ body:not(.fx) .slab{display:none} u.flat.style.visibility = "hidden"; const lis = u.legend ? [...u.legend.children] : []; const n = lis.length; + // per-scene motion signature (occlusion-safe scenes only; default is straight translate). + // "away" is 0 at the hero layout, 1 at collapsed/flown - every signature returns to identity + // at the hero so the assembled slices always match the source image exactly. + const M = u.man.motion, RAD = Math.PI / 180; u.apply = t => { const sg = u.man.stagger || 0, k = u.man.parts.length; u.man.parts.forEach((p, i) => { const pt = sg ? Math.min(1, Math.max(0, t * (1 + sg * (k - 1)) - sg * i)) : t; const e = u.man.reverse ? (1 - pt) : pt; + const away = 1 - e; + let ox = p.dx * away, oy = p.dy * away, extra = ""; + if (M === "tumble") { // aperitif: fragments spin as they fly out + const spin = (p.spin != null ? p.spin : (i % 2 ? 20 : -20)) * away; + extra = " rotate(" + spin.toFixed(2) + "deg)"; + } else if (M === "arc") { // traffic: slices travel a bowed (curved) path + const len = Math.hypot(p.dx, p.dy) || 1; + const bow = (u.man.bow || 9) * 4 * away * (1 - away); + ox += (-p.dy / len) * bow; oy += (p.dx / len) * bow; + } else if (M === "orbit") { // OpenShift: satellites spiral in around the core + const a = (u.man.orbit || 55) * away * RAD, c = Math.cos(a), sn = Math.sin(a); + ox = (p.dx * c - p.dy * sn) * away; oy = (p.dx * sn + p.dy * c) * away; + } u.slabs[i].style.transform = - "translate3d(" + (p.dx * (1 - e)).toFixed(3) + "%," + (p.dy * (1 - e)).toFixed(3) + "%,0)"; + "translate3d(" + ox.toFixed(3) + "%," + oy.toFixed(3) + "%,0)" + extra; }); u.scene.style.transform = "scale(" + (0.95 + 0.05 * t).toFixed(4) + ")"; if (n) lis.forEach((li, i) => li.classList.toggle("lit", t >= 0.9 * ((i + 0.5) / n))); diff --git a/pilot/manifest-orbit.json b/pilot/manifest-orbit.json index e4087b9..c770e26 100644 --- a/pilot/manifest-orbit.json +++ b/pilot/manifest-orbit.json @@ -828,5 +828,7 @@ "dx": 13.85, "dy": 15.36 } - ] + ], + "motion": "orbit", + "orbit": 60 } \ No newline at end of file diff --git a/pilot/manifest-surround.json b/pilot/manifest-surround.json index a0ea6f5..da0fcfd 100644 --- a/pilot/manifest-surround.json +++ b/pilot/manifest-surround.json @@ -675,5 +675,6 @@ "dy": 31.74 } ], - "reverse": true + "reverse": true, + "motion": "tumble" } \ No newline at end of file diff --git a/pilot/manifest-traffic.json b/pilot/manifest-traffic.json index 1a81cc7..635360d 100644 --- a/pilot/manifest-traffic.json +++ b/pilot/manifest-traffic.json @@ -667,5 +667,7 @@ "dy": -11.61 } ], - "stagger": 0.22 + "stagger": 0.22, + "motion": "arc", + "bow": 8 } \ No newline at end of file diff --git a/tools/slice-arc.py b/tools/slice-arc.py index 3041955..50254ba 100644 --- a/tools/slice-arc.py +++ b/tools/slice-arc.py @@ -96,6 +96,8 @@ def emit(name, tag, spec): man["stagger"] = spec["stagger"] if spec.get("reverse"): man["reverse"] = True + for kk in ("motion","bow","orbit"): + if kk in spec: man[kk]=spec[kk] out = ROOT / "pilot" / f"manifest-{tag}.json" out.write_text(json.dumps(man, indent=1)) print(f"manifest-{tag}.json: {len(parts)} parts ->",