Aller au contenu
← 1001 Widgets
N° 009 · ●●○

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.

HTMLCSSJS
Ce qu'on apprend
  • Piloter le CSS avec des variables depuis JS
  • Limiter le calcul avec requestAnimationFrame
  • Gérer souris et doigt avec les Pointer Events
Pourquoi ce langage

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.

Comment ça marche
  1. 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>
  2. 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%)); }
  3. 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}%`);
    });
  4. 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èges

PIÈGE 1 : Une carte qui suit avec retard

Piège

Mauvais : garder la même transition longue pendant le mouvement. La carte suit le pointeur avec un temps de retard mou.

Fix

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

Piège

Mauvais : un calque de reflet par-dessus la carte : plus aucun bouton ne répond.

Fix

Correct : pointer-events: none sur le calque.

PIÈGE 3 : mousemove sur mobile

Piège

Mauvais : écouter mousemove. Au doigt, rien ne bouge.

Fix

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.

PERSONNALISER
  • 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.
OPTIMISATION
  • 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.
VERS REACT

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.

Variantes
Commentaires
0/1000

Chargement des commentaires…