51 lines
2.4 KiB
JavaScript
51 lines
2.4 KiB
JavaScript
// Scroll story: one continuous camera path through the page.
|
|||
|
|
// Given the scroll position and section metrics, returns the scene target.
|
||
|
|
// Framework-agnostic: React preview reads metrics itself, Nuxt feeds it from GSAP ScrollTrigger.
|
||
|
|
const base = { tilt: 0, turn: 0, explode: 0, dim: 1, spin: 0, constellations: 0 };
|
||
|
|
export const STATES = {
|
||
|
|
desktop: {
|
||
|
|
hero: { ...base, cx: 0.27, cy: 0.58, size: 0.78, spin: 1 },
|
||
|
|
eco: { ...base, cx: 0.28, cy: 0.55, size: 0.76 },
|
||
|
|
zij: { ...base, cx: 0.17, cy: 0.52, size: 0.56, constellations: 1 },
|
||
|
|
anatA: { ...base, cx: 0.3, cy: 0.52, size: 0.72 },
|
||
|
|
anatB: { ...base, cx: 0.32, cy: 0.56, size: 0.66, tilt: 0.88, turn: -0.42, explode: 1 },
|
||
|
|
contact: { ...base, cx: 0.27, cy: 0.52, size: 0.78, spin: 1 },
|
||
|
|
},
|
||
|
|
mobile: {
|
||
|
|
hero: { ...base, cx: 0.5, cy: 0.29, size: 1.1, spin: 1 },
|
||
|
|
eco: { ...base, cx: 0.5, cy: 0.245, size: 0.92 },
|
||
|
|
zij: { ...base, cx: 0.5, cy: 0.1, size: 1.5, dim: 0.12, constellations: 1 },
|
||
|
|
anatA: { ...base, cx: 0.5, cy: 0.3, size: 0.95 },
|
||
|
|
anatB: { ...base, cx: 0.5, cy: 0.3, size: 0.8, tilt: 1.05, turn: -0.42, explode: 1 },
|
||
|
|
contact: { ...base, cx: 0.5, cy: 0.3, size: 1.1, spin: 1 },
|
||
|
|
},
|
||
|
|
};
|
||
|
|
const ease = (t) => t * t * (3 - 2 * t);
|
||
|
|
|
||
|
|
export function storyAt(y, m, mobile) {
|
||
|
|
const S = mobile ? STATES.mobile : STATES.desktop;
|
||
|
|
const anat = m.anatTop + (m.anatEnd - m.anatTop) * 0.38;
|
||
|
|
const stops = [
|
||
|
|
[0, S.hero], [m.ecoTop, S.eco], [m.ecoEnd, S.eco],
|
||
|
|
[m.zijTop - m.vh * 0.25, S.zij], [m.anatTop - m.vh * 0.6, S.zij],
|
||
|
|
[m.anatTop, S.anatA], [anat, S.anatB], [m.anatEnd, S.anatB],
|
||
|
|
[m.anatEnd + m.vh * 0.9, S.contact],
|
||
|
|
];
|
||
|
|
let i = 0;
|
||
|
|
while (i < stops.length - 2 && y > stops[i + 1][0]) i++;
|
||
|
|
const [y0, a] = stops[i], [y1, b] = stops[i + 1];
|
||
|
|
const t = ease(Math.min(1, Math.max(0, (y - y0) / Math.max(1, y1 - y0))));
|
||
|
|
const out = {};
|
||
|
|
for (const k in a) out[k] = a[k] + (b[k] - a[k]) * t;
|
||
|
|
|
||
|
|
const pe = (y - m.ecoTop) / Math.max(1, m.ecoEnd - m.ecoTop);
|
||
|
|
const inEco = y > m.ecoTop - m.vh * 0.35 && y < m.ecoEnd + m.vh * 0.35;
|
||
|
|
const ecoIndex = Math.min(14, Math.max(0, Math.floor(pe * 15)));
|
||
|
|
const pa = (y - m.anatTop) / Math.max(1, m.anatEnd - m.anatTop);
|
||
|
|
const focus = pa < 0.4 || pa > 1.15 ? -1 : Math.min(3, Math.floor(((pa - 0.4) / 0.6) * 4));
|
||
|
|
return { target: out, inEco, ecoIndex, focus, pe };
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scroll position that lands on product i inside the pinned ecosystem.
|
||
|
|
export const ecoScrollFor = (i, m) => m.ecoTop + ((i + 0.5) / 15) * (m.ecoEnd - m.ecoTop);
|