const LAPTOP_REST = "transform: rotateY(-14deg) rotateX(8deg) rotateZ(-1deg);"; const MOBILE_REST = "transform: rotateY(-10deg) rotateX(6deg) translateY(-12px);"; /** * Mouse-driven 3D tilt for the hero devices. Pointer events are coalesced into one * layout read + one style write per animation frame. */ export function useHeroParallax() { const laptop = ref(LAPTOP_REST); const mobile = ref(MOBILE_REST); let frame = 0; let stage: HTMLElement | null = null; let clientX = 0; let clientY = 0; const apply = () => { frame = 0; if (!stage) return; const rect = stage.getBoundingClientRect(); const x = (clientX - rect.left) / rect.width - 0.5; const y = (clientY - rect.top) / rect.height - 0.5; const laptopRotateY = -14 + x * 14; const laptopRotateX = 8 - y * 12; const mobileRotateY = -10 + x * 20; const mobileRotateX = 6 - y * 15; const mobileTransY = -12 + y * 18; const mobileTransX = x * 15; laptop.value = `transform: rotateY(${laptopRotateY.toFixed(2)}deg) rotateX(${laptopRotateX.toFixed(2)}deg) rotateZ(-1deg);`; mobile.value = `transform: rotateY(${mobileRotateY.toFixed(2)}deg) rotateX(${mobileRotateX.toFixed(2)}deg) translateY(${mobileTransY.toFixed(2)}px) translateX(${mobileTransX.toFixed(2)}px);`; }; function onMouseMove(e: MouseEvent) { stage = e.currentTarget as HTMLElement; clientX = e.clientX; clientY = e.clientY; if (!frame) frame = requestAnimationFrame(apply); } function onMouseLeave() { if (frame) cancelAnimationFrame(frame); frame = 0; stage = null; laptop.value = LAPTOP_REST; mobile.value = MOBILE_REST; } onBeforeUnmount(() => { if (frame) cancelAnimationFrame(frame); }); return { laptop, mobile, onMouseMove, onMouseLeave }; }