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

Lancer de dé

Un dé en 3D qui roule pour de vrai, en CSS pur, avec l'historique des derniers lancers. Le bon exercice pour comprendre la perspective et les transformations.

HTMLCSSJS
Ce qu'on apprend
  • Construire un cube avec preserve-3d
  • Tourner jusqu'à la bonne face
  • Attendre la fin d'une transition avec transitionend
Pourquoi ce langage

Pourquoi un dé ? Parce qu'il réunit trois choses qu'on sépare trop souvent : un tirage au hasard, une animation CSS en 3D, et le moment précis où le résultat s'affiche.

Le secret : le hasard décide d'abord, l'animation ne fait que montrer le résultat. Jamais l'inverse.

Pourquoi en CSS 3D ? Un cube, six faces, une rotation. Le navigateur fait tout le calcul de perspective.

Comment ça marche
  1. LE HTML

    Un cube de six faces dans une scène. Les faces opposées font toujours 7.

    <div class="scene">
      <div class="de" id="de">
        <div class="face f1">1</div><div class="face f2">2</div><div class="face f3">3</div>
        <div class="face f4">4</div><div class="face f5">5</div><div class="face f6">6</div>
      </div>
    </div>
    <button id="lancer">Lancer</button>
    <output id="resultat"></output>
  2. LE CUBE EN CSS

    Chaque face est tournée, puis avancée de la moitié de la taille du cube.

    .scene { perspective: 600px; }
    .de { width: 100px; height: 100px; position: relative; transform-style: preserve-3d; transition: transform 1.2s cubic-bezier(.2, .8, .2, 1); }
    .face { position: absolute; inset: 0; display: grid; place-items: center; }
    .f1 { transform: rotateY(0deg) translateZ(50px); }
    .f2 { transform: rotateY(90deg) translateZ(50px); }
    .f3 { transform: rotateX(90deg) translateZ(50px); }
    .f4 { transform: rotateX(-90deg) translateZ(50px); }
    .f5 { transform: rotateY(-90deg) translateZ(50px); }
    .f6 { transform: rotateY(180deg) translateZ(50px); }
  3. L'ORIENTATION DE CHAQUE FACE

    Pour montrer une face, on tourne le cube dans le sens inverse de cette face.

    const ORIENT = { 1: [0, 0], 2: [0, -90], 3: [-90, 0], 4: [90, 0], 5: [0, 90], 6: [0, 180] };
  4. LANCER

    Le résultat est tiré d'abord. On ajoute deux tours complets pour que le cube tourne toujours.

    let tours = 0;
    const de = document.getElementById('de');
    const sortie = document.getElementById('resultat');
    document.getElementById('lancer').addEventListener('click', () => {
      const valeur = 1 + crypto.getRandomValues(new Uint32Array(1))[0] % 6;
      tours += 2;
      const [x, y] = ORIENT[valeur];
      sortie.textContent = '';
      de.addEventListener('transitionend', () => { sortie.textContent = valeur; }, { once: true });
      de.style.transform = `rotateX(${x + tours * 360}deg) rotateY(${y + tours * 360}deg)`;
    });
Pièges

PIÈGE 1 : Laisser l'animation décider

Piège

Mauvais : faire tourner le dé au hasard, puis deviner quelle face se retrouve devant. Les calculs d'angles deviennent un cauchemar.

Fix

Correct : tirer le résultat d'abord, puis tourner vers l'orientation connue de cette face.

PIÈGE 2 : Un setTimeout calé sur la durée

Piège

Mauvais : afficher le résultat avec setTimeout(..., 1200). Si quelqu'un change la durée dans le CSS, le chiffre arrive trop tôt ou trop tard.

Fix

Correct : écouter transitionend, avec { once: true } pour ne pas empiler les écouteurs.

PIÈGE 3 : Le dé qui ne tourne plus

Piège

Mauvais : relancer et retomber sur la même face. L'angle ne change pas, il n'y a aucune animation, et transitionend n'arrive jamais.

Fix

Correct : ajouter des tours complets à chaque lancer (tours += 2) : l'angle change toujours.

PERSONNALISER
  • Des points au lieu des chiffres : une grille 3 × 3 par face, avec seulement les cases utiles remplies.
  • Plusieurs dés : une boucle qui crée n cubes et additionne les résultats.
  • Vos couleurs : une variable CSS --face et --chiffre par thème.
OPTIMISATION
  • Avec prefers-reduced-motion, coupez la transition et affichez le résultat tout de suite (sinon transitionend n'arrive jamais).
  • Annoncez le résultat aux lecteurs d'écran avec aria-live sur la sortie.
VERS REACT

La valeur et le nombre de tours vont dans un useState, la rotation se calcule pendant le rendu.

onTransitionEnd sur le cube affiche le résultat, sans setTimeout.

Variantes
Commentaires
0/1000

Chargement des commentaires…