Vai al contenuto
animorio
EN
Torna alla libreria

I dadi sul tavolo

Fisica, 3DGSAP, Solo CSSImpegnativaPeso compresso: Demo 15 KBGSAP 28 KBGratisScarica lo zip

Due dadi veri in CSS 3D lanciati da un bicchiere di cuoio: rotolano sul panno con la fisica vera, rimbalzano, si urtano fra loro e contro la sponda e si fermano su una faccia. La somma arriva in grande con un rimbalzo.

Il codice

Una cartella che funziona da sola: index.html, style.css, script.js e le librerie da CDN. Nelle schede React e Vue la stessa demo è già un componente, che spegne le sue animazioni quando esce dalla pagina.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Dice on the table</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the dice night of a made-up board game bar. On the left the name and the roll; on the right a felt table seen
         from above, with a walnut rail, two dice and a leather cup. The dice, their faces and their shadows are built
         by the script -->
    <section class="side">
      <header class="brand">
        <svg class="brand__mark" viewBox="0 0 40 40" aria-hidden="true">
          <rect x="5" y="5" width="30" height="30" rx="8" transform="rotate(-12 20 20)"/>
          <circle cx="14.5" cy="15" r="2.6"/><circle cx="20" cy="20" r="2.6"/><circle cx="25.5" cy="25" r="2.6"/>
        </svg>
        <p class="brand__name">Pip &amp; Felt<span>Board game bar</span></p>
      </header>

      <div class="score" data-score>
        <div class="score__intro" data-intro>
          <p class="score__kicker">Thursday dice night</p>
          <h1 class="score__title">Roll for the house&nbsp;round</h1>
        </div>
        <div class="score__result" data-result aria-hidden="true">
          <p class="score__kicker" data-name>Natural seven</p>
          <p class="score__sum"><span data-sum>7</span></p>
          <p class="score__parts" data-parts>4 + 3</p>
        </div>
      </div>
      <p class="sr" role="status" aria-live="polite" data-said></p>

      <div class="actions">
        <button class="roll" type="button" data-roll data-autoclic>
          <span class="roll__fill" aria-hidden="true"></span>
          <span class="roll__label" data-roll-label>Roll the dice</span>
        </button>
        <p class="rule">House rule: doubles buy the next round.</p>
      </div>
    </section>

    <div class="scene" data-scene>
      <div class="jolt" data-jolt>
        <div class="table" data-table>
          <div class="rail" aria-hidden="true"></div>
          <div class="felt" aria-hidden="true"><span class="felt__print">Pip &amp; Felt</span></div>
          <div class="wall wall--back" aria-hidden="true"></div>
          <div class="wall wall--front" aria-hidden="true"></div>
          <i class="anchor" data-anchor></i>
          <i class="anchor" data-anchor></i>
        </div>
      </div>
      <div class="cup-place" data-place>
        <div class="cup" data-cup>
          <svg viewBox="0 0 120 150" aria-hidden="true">
            <defs>
              <linearGradient id="dt-leather" x1="0" x2="1">
                <stop offset="0" stop-color="var(--leather-dark)"/>
                <stop offset="0.3" stop-color="var(--leather-light)"/>
                <stop offset="0.55" stop-color="var(--leather)"/>
                <stop offset="1" stop-color="var(--leather-dark)"/>
              </linearGradient>
              <linearGradient id="dt-band" x1="0" x2="1">
                <stop offset="0" stop-color="var(--leather-deep)"/>
                <stop offset="0.32" stop-color="var(--leather-dark)"/>
                <stop offset="1" stop-color="var(--leather-deep)"/>
              </linearGradient>
              <radialGradient id="dt-mouth" cx="0.5" cy="0.2" r="0.8">
                <stop offset="0" stop-color="#140b06"/>
                <stop offset="1" stop-color="#3a2012"/>
              </radialGradient>
            </defs>
            <path class="cup__body" d="M6 18 Q8 90 15 140 Q60 152 105 140 Q112 90 114 18 Z" fill="url(#dt-leather)"/>
            <path d="M9 40 Q60 52 111 40 L110.6 52 Q60 64 9.3 52 Z" fill="url(#dt-band)"/>
            <path d="M13.5 118 Q60 130 106.5 118 L105.6 130 Q60 142 14.4 130 Z" fill="url(#dt-band)"/>
            <path class="cup__stitch" d="M10 45 Q60 57 110 45"/>
            <path class="cup__stitch" d="M14.5 123.5 Q60 135.5 105.5 123.5"/>
            <path class="cup__seam" d="M88 24 Q89 80 92 138"/>
            <path class="cup__stitch" d="M85 26 Q86 80 89 136"/>
            <g class="cup__crest">
              <rect x="44" y="72" width="26" height="26" rx="6" transform="rotate(-12 57 85)"/>
              <circle cx="51.5" cy="80" r="2"/><circle cx="57" cy="85" r="2"/><circle cx="62.5" cy="90" r="2"/>
            </g>
            <ellipse cx="60" cy="18" rx="54" ry="12" fill="url(#dt-mouth)"/>
            <path class="cup__rim" d="M6 18 A54 12 0 0 0 114 18 A54 12 0 0 0 6 18 Z"/>
            <path class="cup__shine" d="M24 30 Q26 80 30 128"/>
          </svg>
        </div>
      </div>
      <p class="hint" data-hint>Tap the table to throw</p>
    </div>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Cambia valori, colori e testi con le manopole e guarda l’animazione rispondere dal vivo. Oppure scrivi tu nel codice. Poi scarica la cartella con le tue modifiche.

36 valori da cambiare

Oppure chiedila a Claude Code

Col connettore di Animorio, Claude Code la prende da qui con tutta la sua guida e la adatta al tuo progetto. Incolla questa richiesta:

Prendi «I dadi sul tavolo» da Animorio e adattala al mio sito

Cosa fa

La serata dei dadi di un bar di giochi da tavolo inventato, Pip & Felt. A sinistra il nome, il titolo Roll for the house round e il pulsante Roll the dice; a destra un tavolo di panno verde visto dall’alto, con la sponda di noce e le sponde interne imbottite, e dietro l’angolo in fondo a sinistra un bicchiere di cuoio con le cuciture, due fasce lavorate e un dado impresso. All’apertura il tavolo sale dal basso, il bicchiere cade sul bancone e si schiaccia, e due dadi d’avorio cadono sul panno, rimbalzano un paio di volte e si fermano. Al clic (sul pulsante o sul tavolo) il bicchiere viene avanti sopra il panno, i dadi ci saltano dentro con un arco e lui li sente arrivare con un piccolo schiacciamento; poi viene agitato a scatti, si alza appena e si rovescia: i dadi escono dalla bocca, rotolano sul panno girando su spigoli e facce, rimbalzano, si urtano fra loro e contro la sponda (il tavolo dà un colpetto da quella parte), rallentano e si posano piatti su una faccia. Il bicchiere si raddrizza dondolando e torna al suo posto. Intanto a sinistra cade la somma, in grande, con un rimbalzo e uno schiacciamento, col nome del tiro (Snake eyes, Natural seven, Boxcars, Hard eight…) e i due numeri. Il risultato non è scelto prima: è la faccia che la fisica lascia in alto. Con un doppio i due dadi fanno un saltello di festa girando di un quarto. Ogni faccia si scurisce quando si gira lontano dalla luce e ogni dado ha la sua ombra sul panno, più larga e chiara quando è in aria.

Come funziona

Un dado in CSS 3D

Ogni dado è un div con transform-style: preserve-3d e sei facce spinte fuori di mezzo lato con translateZ, girate con rotateX e rotateY. I puntini stanno in una griglia di tre per tre, con un’ombra interna che li fa sembrare scavati. Gli angoli arrotondati lascerebbero dei buchi agli spigoli: dentro il dado ci sono tre piani color avorio scuro, uno per asse, che li riempiono e fanno da smusso. Il dado vive dentro il tavolo, inclinato con rotateX, quindi l’alto per lui è l’asse z del tavolo.

La fisica di un corpo rigido

Ogni dado ha posizione, velocità, orientamento (un quaternione) e rotazione. A ogni passo la gravità lo tira giù; se uno degli otto spigoli finisce sotto il panno o oltre la sponda, in quello spigolo nasce un impulso: lungo la normale, col rimbalzo, e di traverso, quanto lo permette l’attrito. Proprio perché l’impulso è nello spigolo e non nel centro, il dado si mette a girare e rotola. Il cubo ha la stessa inerzia su ogni asse (m per lato al quadrato diviso sei), quindi i conti restano semplici. I due dadi fra loro si urtano come due palle, e l’urto li fa anche girare.

Calcolare prima, suonare dopo

Il lancio si calcola tutto al clic, a 480 passi al secondo, e se ne tengono sessanta al secondo: posizione e quaternione di ogni dado. Poi un tween di GSAP su un solo numero scorre i fotogrammi, con la slerp fra uno e l’altro. Così il lancio sta nella stessa timeline del bicchiere e del risultato, la velocità è un timeScale, e gli urti contro la sponda (che il calcolo ha annotato col loro istante) diventano dei call che danno il colpetto al tavolo al momento giusto.

Fermarsi su una faccia

Un dado si considera fermo quando tocca il panno, è lento e la faccia più in alto punta quasi dritta in su. Negli ultimi dodici fotogrammi viene girato del minimo che serve per stare piatto su quella faccia: un quaternione che porta il suo asse più alto sull’asse z. Il numero si legge da lì: l’asse che punta in su dice la faccia. Dopo tre secondi un freno che cresce ferma comunque i dadi, anche con un panno scivoloso e i dadi di gomma.

Luce, ombre e bicchiere

A ogni fotogramma la normale di ogni faccia, girata col dado, si confronta con la direzione della luce: la faccia che si volta riceve un velo scuro più fitto. L’ombra è un gradiente radiale sul panno, spostato lontano dalla luce e più largo e chiaro quanto più il dado è alto. Il bicchiere è un disegno SVG piatto sopra la scena: GSAP muove il suo piede in coordinate del tavolo, due àncore invisibili dentro il tavolo dicono dove cade sullo schermo e quanto è grande lì, e GSAP lo agita e lo rovescia intorno all’angolo in basso. La bocca, ricalcolata nelle misure del tavolo, è il punto da cui partono i dadi.

Valori da cambiare

NomeFileDi partenzaCosa cambia
THROWscript.js1Quanto forte il bicchiere lancia i dadi: con 0.5 si fermano a metà tavolo, con 1.6 sbattono contro la sponda in fondo.
BOUNCEscript.js0.42Quanto rimbalzano sul panno: 0 cadono di peso, 0.7 sembrano di gomma.
FELTscript.js0.5La presa del panno: bassa i dadi scivolano, alta smettono prima di rotolare.
SPINscript.js1Quanto girano i dadi uscendo dal bicchiere.
TILTscript.js48Quanto dall’alto si guarda il tavolo, in gradi: 30 quasi di lato, 62 quasi dall’alto.
SPEEDscript.js1La velocità di tutto il lancio: 2 è il doppio, 0.5 la metà, un rallentatore.

Provali nel Playground

Quando usarla

Per un bar o un negozio di giochi da tavolo, un sito di giochi, un sorteggio o un premio a caso in una promozione, la pagina «scegli tu» di un menu, un gioco da festa, l’attesa di un risultato che deve sembrare davvero lasciato al caso.

Sul telefono

La stessa animazione con le misure adattate: il tavolo diventa più profondo che largo, sotto il titolo e sopra il pulsante, i dadi restano grandi abbastanza da leggere i puntini e il lancio va dal fondo a sinistra verso il davanti a destra. Basta un tocco sul tavolo o sul pulsante.

Con il movimento ridotto

Niente si muove: all’apertura tavolo, testi e dadi compaiono in dissolvenza. Al clic i dadi svaniscono, il lancio si calcola lo stesso e ricompaiono già fermi dove la fisica li ha lasciati, con la somma che compare in dissolvenza.

Trappole

  • Il CSS ha l’asse y in giù e la z verso di noi: un sistema mancino. La fisica usa gli stessi assi e lo stesso prodotto vettoriale, quindi è un mondo allo specchio, ma la fisica allo specchio è ancora fisica: basta non mescolare formule pensate per gli assi destri.
  • Con un passo fisso di 1/60 di secondo i dadi attraversavano la sponda e tremavano da fermi: i passi sono 480 al secondo, e quando uno spigolo tocca appena (meno di 1,2 lati al secondo) il rimbalzo è zero, così un dado posato non saltella.
  • Un dado che si ferma in bilico su uno spigolo non dice nessun numero: un dado è fermo solo se la faccia più alta punta quasi dritta in su, e negli ultimi fotogrammi viene girato del poco che manca per stare piatto.
  • Con il panno scivoloso e i dadi di gomma il lancio poteva durare all’infinito: dopo tre secondi un freno che cresce ferma tutto, così il bicchiere e la somma non aspettano mai troppo.
  • Il bicchiere disegnato su un piano dentro il tavolo 3D, rovesciato, veniva tagliato di netto: Chrome ordina male un piano che attraversa gli altri. Ora sta fuori dal 3D, disegnato piatto sopra la scena, e due àncore invisibili nel tavolo (lette con getBoundingClientRect) gli dicono dove stare e quanto essere grande.
  • Un filtro blur sulle ombre dentro un contesto 3D rendeva ogni fotogramma lentissimo, e un’opacità sul dado intero lo schiacciava piatto (opacity rompe preserve-3d): il dado sfuma faccia per faccia con una variabile CSS, e le ombre sono solo gradienti radiali con più fermate, che sembrano sfocati senza costare niente.

Librerie

GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Instrument Sans (font variabile) 5.3.0.

Compressi con gzip, i tre file della demo pesano 15 KB; le librerie: GSAP 28 KB.

Collaudo

Provata il 10 ottobre 2026

  • Computer: Fatto
  • Telefono: Fatto
  • Movimento ridotto: Fatto
  • Safari su iPhone: Da fare

Licenza d’uso

Puoi usarla nei tuoi progetti e in quelli dei tuoi clienti. Non puoi rivenderla né ridistribuirla così com’è. GSAP resta sotto la sua licenza Standard. Licenza completa.

Tutte le animazioni