Carte à reflet
Une carte qui penche vers le pointeur, avec un reflet qui glisse sur sa surface. L'effet des cartes de cette page, démonté pièce par pièce.
- Piloter le CSS avec des variables depuis JS
- Limiter le calcul avec requestAnimationFrame
- Gérer souris et doigt avec les Pointer Events
Pourquoi une carte à reflet ? C'est l'effet des cartes de ce site : la carte s'incline vers le pointeur et une bande de lumière glisse sur sa surface, comme sur une carte holographique.
Tout tient dans trois variables CSS mises à jour par JavaScript : deux angles d'inclinaison et la position du reflet. Le CSS fait le reste.
Pourquoi des variables CSS ? JavaScript ne touche jamais au style directement : il donne trois nombres, et le CSS décide de ce qu'il en fait.
LE HTML
Le reflet est un calque posé sur la carte.
<div class="carte" id="carte"> <h3>N° 009</h3> <div class="reflet"></div> </div>
LE CSS
L'inclinaison et le reflet lisent des variables. Par défaut, la carte est à plat.
.carte { --rx: 0deg; --ry: 0deg; --px: 50%; position: relative; overflow: hidden; transform: perspective(800px) rotateX(var(--rx)) rotateY(var(--ry)); transition: transform .5s cubic-bezier(.16, 1, .3, 1); } .carte.bouge { transition: transform .08s linear; } .reflet { position: absolute; inset: 0; pointer-events: none; background: linear-gradient(105deg, transparent calc(var(--px) - 20%), rgba(255, 255, 255, .18) var(--px), transparent calc(var(--px) + 20%)); }SUIVRE LE POINTEUR
On ramène la position du pointeur entre -0,5 et 0,5, le centre de la carte valant 0.
const carte = document.getElementById('carte'); carte.addEventListener('pointermove', e => { const r = carte.getBoundingClientRect(); const x = (e.clientX - r.left) / r.width - 0.5; const y = (e.clientY - r.top) / r.height - 0.5; carte.classList.add('bouge'); carte.style.setProperty('--ry', `${x * 16}deg`); carte.style.setProperty('--rx', `${-y * 16}deg`); carte.style.setProperty('--px', `${(x + 0.5) * 100}%`); });REVENIR À PLAT
On retire la classe bouge : la transition longue reprend et la carte se repose en douceur.
carte.addEventListener('pointerleave', () => { carte.classList.remove('bouge'); carte.style.setProperty('--rx', '0deg'); carte.style.setProperty('--ry', '0deg'); carte.style.setProperty('--px', '50%'); });
PIÈGE 1 : Une carte qui suit avec retard
Mauvais : garder la même transition longue pendant le mouvement. La carte suit le pointeur avec un temps de retard mou.
Correct : une transition très courte pendant le mouvement (la classe bouge), et la transition longue seulement au retour à plat.
PIÈGE 2 : Le reflet qui bloque les clics
Mauvais : un calque de reflet par-dessus la carte : plus aucun bouton ne répond.
Correct : pointer-events: none sur le calque.
PIÈGE 3 : mousemove sur mobile
Mauvais : écouter mousemove. Au doigt, rien ne bouge.
Correct : pointermove couvre la souris, le doigt et le stylet. Ajoutez touch-action: none sur la carte pour que le doigt ne fasse pas défiler la page.
- L'angle maximal : changez le 16 pour une carte plus calme ou plus vive.
- La couleur du reflet : remplacez le blanc par une teinte, ou superposez deux bandes de couleurs différentes pour un effet holographique.
- Une ombre décalée à l'opposé de l'inclinaison renforce le relief.
- Regroupez les mises à jour avec requestAnimationFrame : une seule écriture par image, même si le pointeur envoie plus d'événements (c'est ce que fait la démo).
- Avec prefers-reduced-motion, gardez le reflet mais supprimez l'inclinaison.
Un useRef sur la carte, et des écouteurs qui écrivent les variables CSS directement.
Pas de useState pour la position : sinon chaque mouvement redessine tout le composant.
Chargement des commentaires…