75 lines
2.5 KiB
JavaScript
75 lines
2.5 KiB
JavaScript
/* ================================================
|
||
Shoe Showcase – Interactive Mouse-Driven Animation
|
||
================================================
|
||
- Background panels expand/contract based on mouse X
|
||
- Phone mockups fade in/out (only phones, text stays)
|
||
- On mouse leave → smoothly resets to 50/50
|
||
================================================ */
|
||
|
||
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');
|
||
|
||
let targetRatio = 0.5;
|
||
let currentRatio = 0.5;
|
||
const LERP_SPEED = 0.1;
|
||
|
||
function lerp(a, b, t) {
|
||
return a + (b - a) * t;
|
||
}
|
||
|
||
function clamp(v, lo, hi) {
|
||
return Math.min(hi, Math.max(lo, v));
|
||
}
|
||
|
||
/* ---- mouse tracking ---- */
|
||
showcase.addEventListener('mousemove', (e) => {
|
||
const rect = showcase.getBoundingClientRect();
|
||
targetRatio = clamp((e.clientX - rect.left) / rect.width, 0, 1);
|
||
});
|
||
|
||
showcase.addEventListener('mouseleave', () => {
|
||
targetRatio = 0.5;
|
||
});
|
||
|
||
/* ---- animation loop ---- */
|
||
function animate() {
|
||
currentRatio = lerp(currentRatio, targetRatio, LERP_SPEED);
|
||
|
||
const r = currentRatio;
|
||
|
||
/* 1. Background panel flex (full-width decorative) */
|
||
const redFlex = lerp(3.5, 0.3, r);
|
||
const blackFlex = lerp(0.3, 3.5, r);
|
||
|
||
bgRed.style.flex = redFlex;
|
||
bgBlack.style.flex = blackFlex;
|
||
|
||
/* 2. Phone mockup scale / opacity / translate */
|
||
const bias = (0.5 - r) * 2; // +1 red, -1 black
|
||
|
||
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;
|
||
|
||
phoneRed.style.transform = `scale(${redScale}) translateX(${redShiftX}px)`;
|
||
phoneRed.style.opacity = redOpacity;
|
||
|
||
phoneBlack.style.transform = `scale(${blackScale}) translateX(${blackShiftX}px)`;
|
||
phoneBlack.style.opacity = blackOpacity;
|
||
|
||
/* 3. Text always stays visible — no opacity changes */
|
||
|
||
requestAnimationFrame(animate);
|
||
}
|
||
|
||
animate();
|
||
});
|