1 2/** 3 * Applica un tilt fluido alla card: 4 * - targetDeg â dove vorremmo arrivare 5 * - currentDeg â valore attuale che si avvicina al target con un easing 6 */ 7function bindSmoothTilt(card, maxDeg = 6, easing = 0.12) { 8 let targetDeg = 0; 9 let currentDeg = 0; 10 let rafId = null; 11 12 // loop di animazione âmorbidoâ 13 const animate = () => { 14 // avviciniamo currentDeg a targetDeg in piccole frazioni (lerp) 15 currentDeg += (targetDeg - currentDeg) * easing; 16 17 // aggiorna solo se la differenza è apprezzabile (evita micro-scritture) 18 if (Math.abs(targetDeg - currentDeg) > 0.01) { 19 card.style.transform = `translateY(-4px) rotateZ(${currentDeg}deg)`; 20 rafId = requestAnimationFrame(animate); 21 } else { 22 cancelAnimationFrame(rafId); 23 rafId = null; 24 } 25 }; 26 27 card.addEventListener('mousemove', (e) => { 28 const { left, width } = card.getBoundingClientRect(); 29 const ratio = (e.clientX - left) / width - 0.5; // -0.5 ⦠+0.5 30 targetDeg = ratio * 2 * maxDeg; // -maxDeg ⦠+maxDeg 31 if (!rafId) rafId = requestAnimationFrame(animate); 32 card.style.boxShadow = '0 14px 26px rgba(0,0,0,.2)'; 33 }); 34 35 card.addEventListener('mouseleave', () => { 36 targetDeg = 0; // torna neutro 37 if (!rafId) rafId = requestAnimationFrame(animate); 38 // ombra e transizione di âatterraggioâ 39 card.style.transition = 'transform .35s ease-out, box-shadow .35s ease'; 40 card.style.boxShadow = ''; 41 }); 42 43 card.addEventListener('mouseenter', () => { 44 // tolgo la transizione per non rallentare i movimenti del mouse 45 card.style.transition = 'box-shadow .3s ease'; 46 }); 47} 48 49// attacco lo script a tutte le card 50document.querySelectorAll('.promo-card').forEach(c =>
50 bindSmoothTilt(c));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.