Vai al contenuto
animorio
EN
Torna alla libreria

Il castello di carte

Fisica, SVGGSAP, Solo CSSImpegnativaPeso compresso: Demo 11 KBGSAP 28 KBMatter.js 25 KBGratisScarica lo zip

Un castello di carte da gioco si costruisce da solo, carta per carta, con le carte che arrivano planando e si appoggiano a coppie tremando; un clic o un soffio del puntatore lo fa crollare con la fisica vera, le carte si ammucchiano e poi il castello si rifà.

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>The house of cards</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bodoni-moda@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="scena" data-stage data-autoclic tabindex="0" role="button" aria-label="A house of cards: click to knock it down">
    <!-- the drawings every card uses: the back of the deck and four fronts, drawn upright and turned to fit the card -->
    <svg class="disegni" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <pattern id="lattice" patternUnits="userSpaceOnUse" width="8" height="8" patternTransform="rotate(45)">
          <path class="dorso-rete" d="M0 0V8M0 0H8" />
        </pattern>
        <path id="pip-heart" d="M0 8C-9 1-11-4-11-7C-11-11-8-13-5.5-13C-3-13-1-11.5 0-9.5C1-11.5 3-13 5.5-13C8-13 11-11 11-7C11-4 9 1 0 8Z" />
        <path id="pip-diamond" d="M0-12C2.6-7 5.6-3 8.4 0C5.6 3 2.6 7 0 12C-2.6 7-5.6 3-8.4 0C-5.6-3-2.6-7 0-12Z" />
        <path id="pip-spade" d="M0-13C-9-5-11 0-11 3C-11 7-8 9-5.5 9C-3.6 9-2 8-1.2 6.6C-1.4 9-2.6 11.2-4.6 12.6H4.6C2.6 11.2 1.4 9 1.2 6.6C2 8 3.6 9 5.5 9C8 9 11 7 11 3C11 0 9-5 0-13Z" />
        <path id="pip-club" d="M0-13C3.4-13 5.8-10.4 5.8-7.2C5.8-5.8 5.4-4.6 4.6-3.6C5.2-3.8 5.8-3.9 6.4-3.9C9.6-3.9 12-1.4 12 1.8C12 5 9.6 7.6 6.4 7.6C4.4 7.6 2.6 6.6 1.6 5C1.8 8.2 2.8 10.8 4.6 12.6H-4.6C-2.8 10.8-1.8 8.2-1.6 5C-2.6 6.6-4.4 7.6-6.4 7.6C-9.6 7.6-12 5-12 1.8C-12-1.4-9.6-3.9-6.4-3.9C-5.8-3.9-5.2-3.8-4.6-3.6C-5.4-4.6-5.8-5.8-5.8-7.2C-5.8-10.4-3.4-13 0-13Z" />
        <path id="star" d="M0-6C.8-1.6 1.6-.8 6 0C1.6.8.8 1.6 0 6C-.8 1.6-1.6.8-6 0C-1.6-.8-.8-1.6 0-6Z" />

        <!-- the back of the deck: a lattice, a frame, and a moon with a star in an oval -->
        <symbol id="back" viewBox="0 0 100 70">
          <g transform="translate(100 0) rotate(90)">
            <rect class="dorso-carta" width="70" height="100" rx="5" />
            <rect class="dorso" x="4" y="4" width="62" height="92" rx="3" />
            <rect fill="url(#lattice)" x="7" y="7" width="56" height="86" rx="2" />
            <rect class="dorso-filo" x="7" y="7" width="56" height="86" rx="2" />
            <rect class="dorso-filo dorso-filo--fine" x="9.5" y="9.5" width="51" height="81" rx="1.5" />
            <ellipse class="dorso dorso-filo" cx="35" cy="50" rx="17" ry="23" />
            <ellipse class="dorso-filo dorso-filo--fine" cx="35" cy="50" rx="14" ry="20" />
            <path class="dorso-oro" d="M39.5 37.5C33 37.5 28.5 43 28.5 49.5C28.5 56 33 61.5 39.5 61.5C36.6 62.8 33.4 63.2 30.2 62C25.4 60 22.6 55 22.6 49.5C22.6 44 25.4 39 30.2 37C33.4 35.8 36.6 36.2 39.5 37.5Z" />
            <use class="dorso-oro" href="#star" transform="translate(41 47) scale(.9)" />
            <use class="dorso-oro" href="#star" transform="translate(44.5 57) scale(.45)" />
            <use class="dorso-oro" href="#star" transform="translate(14 14) scale(.55)" />
            <use class="dorso-oro" href="#star" transform="translate(56 14) scale(.55)" />
            <use class="dorso-oro" href="#star" transform="translate(14 86) scale(.55)" />
            <use class="dorso-oro" href="#star" transform="translate(56 86) scale(.55)" />
          </g>
        </symbol>

        <!-- four fronts: an ace and three court letters, each with one big pip -->
        <symbol id="front-a" viewBox="0 0 100 70">
          <g transform="translate(100 0) rotate(90)">
            <rect class="fronte" width="70" height="100" rx="5" />
            <rect class="fronte-filo" x="3.5" y="3.5" width="63" height="93" rx="3" />
            <use class="seme-nero" href="#pip-spade" transform="translate(35 50) scale(1.55)" />
            <text class="indice seme-nero" x="9" y="16">A</text>
            <use class="seme-nero" href="#pip-spade" transform="translate(10.5 24) scale(.32)" />
            <g transform="rotate(180 35 50)">
              <text class="indice seme-nero" x="9" y="16">A</text>
              <use class="seme-nero" href="#pip-spade" transform="translate(10.5 24) scale(.32)" />
            </g>
          </g>
        </symbol>
        <symbol id="front-k" viewBox="0 0 100 70">
          <g transform="translate(100 0) rotate(90)">
            <rect class="fronte" width="70" height="100" rx="5" />
            <rect class="fronte-filo" x="3.5" y="3.5" width="63" height="93" rx="3" />
            <use class="seme-rosso" href="#pip-heart" transform="translate(35 51) scale(1.5)" />
            <text class="indice seme-rosso" x="9" y="16">K</text>
            <use class="seme-rosso" href="#pip-heart" transform="translate(10.5 24) scale(.32)" />
            <g transform="rotate(180 35 50)">
              <text class="indice seme-rosso" x="9" y="16">K</text>
              <use class="seme-rosso" href="#pip-heart" transform="translate(10.5 24) scale(.32)" />
            </g>
          </g>
        </symbol>
        <symbol id="front-q" viewBox="0 0 100 70">
          <g transform="translate(100 0) rotate(90)">
            <rect class="fronte" width="70" height="100" rx="5" />
            <rect class="fronte-filo" x="3.5" y="3.5" width="63" height="93" rx="3" />
            <use class="seme-rosso" href="#pip-diamond" transform="translate(35 50) scale(1.6)" />
            <text class="indice seme-rosso" x="9" y="16">Q</text>
            <use class="seme-rosso" href="#pip-diamond" transform="translate(10.5 24) scale(.32)" />
            <g transform="rotate(180 35 50)">
              <text class="indice seme-rosso" x="9" y="16">Q</text>
              <use class="seme-rosso" href="#pip-diamond" transform="translate(10.5 24) scale(.32)" />
            </g>
          </g>
        </symbol>
        <symbol id="front-j" viewBox="0 0 100 70">
          <g transform="translate(100 0) rotate(90)">
            <rect class="fronte" width="70" height="100" rx="5" />
            <rect class="fronte-filo" x="3.5" y="3.5" width="63" height="93" rx="3" />
            <use class="seme-nero" href="#pip-club" transform="translate(35 50) scale(1.45)" />
            <text class="indice seme-nero" x="9" y="16">J</text>
            <use class="seme-nero" href="#pip-club" transform="translate(10.5 24) scale(.32)" />
            <g transform="rotate(180 35 50)">
              <text class="indice seme-nero" x="9" y="16">J</text>
              <use class="seme-nero" href="#pip-club" transform="translate(10.5 24) scale(.32)" />
            </g>
          </g>
        </symbol>
      </defs>
    </svg>

    <header class="testa">
      <p class="sopra" data-head>Ottoline Vane presents</p>
      <h1 class="titolo" data-head>The House of Cards</h1>
    </header>

    <!-- the table, turned a little so the backs of the cards show; the script deals the cards onto it -->
    <div class="palco" data-view>
      <div class="mondo" data-world>
        <div class="tavolo" data-table></div>
      </div>
    </div>

    <!-- the puff of air that knocks it down -->
    <svg class="soffio" data-gusts aria-hidden="true" xmlns="http://www.w3.org/2000/svg"></svg>

    <p class="nota" aria-live="polite"><span data-hint>Dealing the house</span></p>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/matter-js@0.20.0/build/matter.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.

25 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 «Il castello di carte» da Animorio e adattala al mio sito

Cosa fa

Su un tavolo di panno verde, visto un po’ dall’alto e girato di lato, arrivano le carte di un mazzo inventato, quello del mago Ottoline Vane: dorso rosso scuro con una rete di rombi, cornice d’oro e una luna con le stelle in un ovale. Arrivano dall’alto una alla volta e planano come foglie, dondolando e ondeggiando di lato. Due carte si fermano poco sopra il loro posto, poi scendono insieme e si appoggiano l’una all’altra con un tremolio; quando una fila di coppie è pronta, sopra si posa una carta piatta, che rimbalza appena, e le coppie sotto fremono. Così, piano per piano, si alza un castello di quattro piani e ventisei carte; qualcuna mostra il davanti, un asso o una figura col seme disegnato. Muovendo piano il mouse sopra il castello, tutto si inclina un poco e torna a posto con un ondeggiamento elastico. Un clic, un tocco o una passata veloce del mouse (il soffio) lo fa crollare: tre filetti d’aria passano sulle carte, e da quella parte le carte si staccano per prime, a onda; scivolano sul panno, ruotano, planano e si ammucchiano. Dopo un momento ogni carta si solleva dal mucchio e torna al suo posto planando, e il castello si rifà, più in fretta della prima volta.

Come funziona

Carte vere in 3D

Il tavolo e le carte stanno in un mondo CSS 3D: un contenitore con transform-style preserve-3d, girato con rotateX e rotateY dentro un palco con la prospettiva. Ogni carta è un div lungo quanto la carta e profondo quanto è larga, con due facce schiena contro schiena (backface-visibility hidden): rotate porta l’inclinazione della carta, rotateX(90deg) la mette di taglio, così il dorso si vede di tre quarti. I disegni (dorso, assi e figure, semi) sono simboli SVG fatti da noi, usati con use.

Un numero per carta

Ogni carta è un oggetto con x, y, z, inclinazione e opacità: GSAP muove quei numeri, la fisica li riscrive, e a ogni giro del ticker una sola funzione li copia nei transform. Sotto ogni carta c’è un’ombra sul panno, larga quanto la carta vista dall’alto e più chiara quanto più la carta è in alto; e una carta in piedi è più scura di una sdraiata, che prende la luce della lampada.

Le carte che planano

Le posizioni del castello si calcolano in lunghezze di carta: l’inclinazione decide quanto si allargano i piedi di una coppia, la carta piatta sta sulle due punte vicine, il piano dopo poggia su di lei. Ogni carta arriva con una timeline: x con dei keyframe che oscillano di lato, y che rallenta, l’inclinazione che dondola e si smorza. Le due carte di una coppia si fermano un attimo sopra il loro posto, scendono insieme e tremano con dei keyframe piccoli intorno alla posizione giusta.

Il crollo con Matter.js

Al clic ogni carta diventa un rettangolo sottile in Matter.js, fermo (static) dove si trova. Le carte si liberano a onda, prima le più vicine al colpo, con una spinta che cala con la distanza ma non è mai zero, così crolla tutto. Il panno all’inizio è scivoloso, poi fa presa e il mucchio si ferma. Per ogni passo la velocità perpendicolare alla carta viene frenata, quella lungo la carta no: una carta di piatto cade piano e plana di lato, come quelle vere.

Il soffio e la ricostruzione

Una passata veloce del mouse sopra il castello (più di 2,6 pixel al millisecondo) fa lo stesso del clic, nella direzione del movimento; una lenta lo fa solo inclinare. La fisica avanza a passi fissi di 1/120 di secondo, e quando le carte sono ferme (o dopo pochi secondi) il mondo di Matter si svuota. Dopo un attimo una timeline nuova solleva ogni carta dal mucchio verso di noi e la riporta al suo posto con lo stesso volo della prima volta, più veloce.

Valori da cambiare

NomeFileDi partenzaCosa cambia
LEVELSscript.js4I piani del castello, da 2 a 5: con quattro piani le carte sono ventisei.
LEANscript.js18Di quanti gradi ogni carta pende dalla verticale.
DEAL_SPEED / FLUTTERscript.js1 / 1La velocità della costruzione e quanto le carte dondolano planando.
BLOW / SLIDEscript.js1 / 0.03La forza del colpo e quanto è scivoloso il panno mentre crolla.
RESTscript.js1.6I secondi in cui il mucchio resta lì prima che il castello si rifaccia.
TURNscript.js28Di quanti gradi è girato il tavolo, per vedere i dorsi delle carte.
--back, --gold, --feltstyle.css#8f1f2c, #e7c47a, #1d5446Il dorso delle carte, l’oro dei disegni e il panno del tavolo.

Provali nel Playground

Quando usarla

Per la pagina di un mago, di un club di giochi da tavolo o di una sala da gioco, per un 404 («qui è crollato tutto») o per una pagina che parla di fragilità e di rischio. È un gioco da guardare: il contenuto vero va accanto.

Sul telefono

Stesse animazioni e stessa fisica: le carte si misurano sullo schermo, quindi sul telefono il castello sta in larghezza e in altezza. Il soffio è solo del mouse; col dito basta un tocco.

Con il movimento ridotto

Il castello è già costruito. Un clic lo fa sparire in dissolvenza e al suo posto compare il mucchio già fermo (la fisica si calcola tutta in un colpo); il clic dopo, sempre in dissolvenza, rimette il castello.

Trappole

  • Un castello di rettangoli in Matter.js, lasciato andare così com’è, resta in piedi: il motore è bravo a tenere fermi i mucchi. Per farlo crollare serve una spinta su ogni carta (anche piccola) e un panno scivoloso nel primo secondo.
  • Le carte non devono toccarsi quando nasce la fisica: due rettangoli che si sovrappongono anche di un pixel si respingono di colpo. Le posizioni lasciano mezzo spessore di carta di gioco fra le due punte.
  • Una carta sottile e veloce può attraversarne un’altra in un passo solo: passi fissi piccoli (1/120 di secondo) e più iterazioni lo evitano quasi sempre.
  • La fisica va avanzata dal ticker di GSAP, non da un suo requestAnimationFrame: così rallenta e si ferma insieme alle timeline, e un watchdog fatto con gsap.delayedCall chiude il crollo anche se una carta continua a strisciare.
  • In CSS 3D due piani che si incrociano si tagliano a vicenda: le carte stanno tutte sullo stesso piano z e la fisica impedisce che si sovrappongano, così non succede.

Librerie

GSAP 3.15.0, Matter.js 0.20.0, Bodoni Moda (font variabile) 5.3.0.

Compressi con gzip, i tre file della demo pesano 11 KB; le librerie: GSAP 28 KB, Matter.js 25 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