84 lines
2.9 KiB
JavaScript
84 lines
2.9 KiB
JavaScript
/* ================================================
|
||
Shoe Showcase – Interactive GSAP Animation
|
||
================================================
|
||
|
||
- Uses GSAP for smooth easing and interpolation
|
||
================================================ */
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const showcase = document.getElementById('showcase');
|
||
const bgRed = document.getElementById('bg-red');
|
||
const bgBlack = document.getElementById('bg-black');
|
||
const phoneRed = document.getElementById('phone-red');
|
||
const phoneBlack = document.getElementById('phone-black');
|
||
const contentLeft = document.getElementById('content-left');
|
||
const contentRight = document.getElementById('content-right');
|
||
|
||
function clamp(v, lo, hi) {
|
||
return Math.min(hi, Math.max(lo, v));
|
||
}
|
||
|
||
function lerp(a, b, t) {
|
||
return a + (b - a) * t;
|
||
}
|
||
|
||
function animateToRatio(ratio) {
|
||
// ratio is 0 (far left) to 1 (far right). Center is 0.5.
|
||
|
||
// 1. Calculate flex values for background panels
|
||
const redFlex = lerp(3.5, 0.3, ratio);
|
||
const blackFlex = lerp(0.3, 3.5, ratio);
|
||
|
||
// 2. Calculate bias for phones (+1 when red dominates, -1 when black dominates)
|
||
const bias = (0.5 - ratio) * 2;
|
||
|
||
const redScale = 1 + clamp(bias, 0, 1) * 0.3;
|
||
const redOpacity = clamp(1 + bias * 3, 0, 1);
|
||
const redShiftX = clamp(bias, 0, 1) * 60;
|
||
|
||
const blackScale = 1 + clamp(-bias, 0, 1) * 0.3;
|
||
const blackOpacity = clamp(1 - bias * 3, 0, 1);
|
||
const blackShiftX = clamp(-bias, 0, 1) * -60;
|
||
|
||
// 3. Animate using GSAP
|
||
// Using overwrite: "auto" ensures that new mouse movements smoothly override old animations
|
||
const animConfig = { duration: 0.5, ease: "power2.out", overwrite: "auto" };
|
||
|
||
gsap.to(bgRed, { flexGrow: redFlex, ...animConfig });
|
||
gsap.to(bgBlack, { flexGrow: blackFlex, ...animConfig });
|
||
|
||
gsap.to(phoneRed, {
|
||
scale: redScale,
|
||
x: redShiftX,
|
||
opacity: redOpacity,
|
||
...animConfig
|
||
});
|
||
|
||
gsap.to(phoneBlack, {
|
||
scale: blackScale,
|
||
x: blackShiftX,
|
||
opacity: blackOpacity,
|
||
...animConfig
|
||
});
|
||
|
||
gsap.to(contentLeft, { opacity: redOpacity, ...animConfig });
|
||
gsap.to(contentRight, { opacity: blackOpacity, ...animConfig });
|
||
}
|
||
|
||
// Set initial center state
|
||
gsap.set(bgRed, { flexGrow: 1 });
|
||
gsap.set(bgBlack, { flexGrow: 1 });
|
||
|
||
/* ---- Mouse tracking ---- */
|
||
showcase.addEventListener('mousemove', (e) => {
|
||
const rect = showcase.getBoundingClientRect();
|
||
const ratio = clamp((e.clientX - rect.left) / rect.width, 0, 1);
|
||
animateToRatio(ratio);
|
||
});
|
||
|
||
// Spring back to center on leave
|
||
showcase.addEventListener('mouseleave', () => {
|
||
animateToRatio(0.5);
|
||
});
|
||
});
|