
Il banner dei cookie
Consenso, Transizioni, SVG, Fisica
NuovaGratis
Le fatture di uno studio in una tabella vera: le righe scelte stanno in una sola forma elastica che esce dalla casella spuntata, si allunga sulla riga dopo, scende per tutta la tabella e si strappa in due quando si lascia una riga in mezzo. Il piede si srotola nella barra delle azioni; le righe cancellate cadono in una botola nel tavolo mentre le altre ci scivolano sopra, e Undo le fa risalire.
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 trapdoor table</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the trapdoor table: a studio's invoices. The rows picked are held by one elastic shape that grows out of the
box ticked; the footer rolls out into a bar of actions; deleted rows fall through a trapdoor in the table
while the rows below slide over the hole, and Undo swings them back up -->
<section class="ledger" aria-labelledby="ledger-title" data-ledger data-autoclic>
<header class="ledger__head">
<h2 class="ledger__title" id="ledger-title">Invoices</h2>
<p class="ledger__note">September and October</p>
</header>
<div class="frame" data-frame>
<div class="pit" aria-hidden="true" data-pit></div>
<table class="table" aria-labelledby="ledger-title" data-table>
<thead>
<tr>
<th class="pick" scope="col">
<label class="check" data-all-check>
<input type="checkbox" data-all>
<span class="check__box" aria-hidden="true"><svg viewBox="0 0 24 24"><path class="check__tick" pathLength="1" d="M6.2 12.4l3.9 3.9 7.7-8.2"/><path class="check__dash" pathLength="1" d="M7.5 12h9"/></svg></span>
<span class="sr">Select all invoices</span>
</label>
</th>
<th class="client" scope="col">Client</th>
<th class="col-inv" scope="col">Invoice</th>
<th class="col-due" scope="col">Due</th>
<th class="num" scope="col">Amount</th>
<th class="state-cell" scope="col">Status</th>
<th class="more" scope="col"><span class="sr">Details</span></th>
</tr>
</thead>
<tbody data-rows></tbody>
</table>
<svg class="band" aria-hidden="true" data-band>
<defs>
<linearGradient id="ledger-sweep" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="1" y2="0" data-sweep>
<stop offset="0" class="band__stop"/>
<stop offset="0.5" class="band__stop band__stop--deep"/>
<stop offset="1" class="band__stop"/>
</linearGradient>
</defs>
<path class="band__shape" data-band-path/>
</svg>
</div>
<div class="foot" data-foot>
<p class="sum" data-sum>
<span><span class="roll" data-sum-count>6</span> invoices</span>
<span class="sum__due"><span class="roll" data-sum-due>€0.00</span> outstanding</span>
</p>
<div class="bar" data-bar>
<span class="bar__bg" aria-hidden="true" data-bar-bg></span>
<div class="bar__set" role="group" aria-label="Selected invoices" data-set="actions">
<button class="bar__icon" type="button" data-clear>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 7l10 10M17 7 7 17"/></svg>
<span class="sr">Clear selection</span>
</button>
<p class="bar__count"><span class="roll" data-picked>0</span> selected <span class="bar__sum roll" data-picked-sum>€0.00</span></p>
<span class="bar__rule" aria-hidden="true"></span>
<button class="bar__btn" type="button" data-act="paid">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.6l4.3 4.3L19 7.3"/></svg>
<span class="bar__label">Mark as paid</span>
</button>
<button class="bar__btn bar__btn--export" type="button" data-act="export">
<span class="bar__glyph" aria-hidden="true">
<svg class="bar__glyph-a" viewBox="0 0 24 24"><path d="M12 4.5v10M7.6 10.4 12 14.8l4.4-4.4M5 15.5v3.5h14v-3.5"/></svg>
<svg class="bar__glyph-b" viewBox="0 0 24 24"><path d="M5 12.6l4.3 4.3L19 7.3"/></svg>
</span>
<span class="bar__label roll" data-export-label>Export</span>
</button>
<button class="bar__btn bar__btn--danger" type="button" data-act="delete">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4.5 7h15M9.5 4.5h5M6.6 7l.9 12.1a1.2 1.2 0 0 0 1.2 1.1h6.6a1.2 1.2 0 0 0 1.2-1.1L17.4 7M10.2 10.8v5.6M13.8 10.8v5.6"/></svg>
<span class="bar__label">Delete</span>
</button>
</div>
<div class="bar__set bar__set--toast" data-set="toast">
<p class="bar__msg" data-msg></p>
<button class="bar__btn bar__btn--undo" type="button" data-undo>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9.2 5.6 5.4 9.4l3.8 3.8M5.8 9.4h8.4a4.6 4.6 0 0 1 0 9.2h-3"/></svg>
<span class="bar__label">Undo</span>
</button>
<span class="bar__time" aria-hidden="true" data-time></span>
</div>
</div>
</div>
<p class="sr" aria-live="polite" data-live></p>
</section>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>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.
118 valori da cambiare
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 «La tabella con la botola» da Animorio e adattala al mio sitoOgni riga ha un pezzo della forma: il bordo di sopra e quello di sotto come parti dell'altezza della riga, i due lati in pixel dai lati della tabella, tutti e quattro su molle smorzate (x'' = −k(x − meta) − c·x', a passi di 1/240 di secondo, il passo del fotogramma limitato a 1/30). A ogni fotogramma i pezzi che si toccano diventano un contorno solo, giù per il lato destro e su per il sinistro, con ogni angolo arrotondato, anche quelli rientranti, quanto permettono i lati vicini. Il path SVG sta sopra la tabella in mix-blend-mode: multiply, così le righe restano piene e la forma le tinge sotto le parole. Due blocchi che stanno per toccarsi, o appena separati, hanno gli angoli dritti, e si arrotondano man mano che lo spazio fra loro si apre: unirsi e dividersi non salta mai.
Un pezzo nuovo parte dal quadratino della sua casella; se la riga sopra è già scelta parte dal suo bordo e ci resta attaccato mentre cresce, così la forma si allunga senza staccarsi, e scegliendo tutte le righe ognuna parte dal bordo della precedente un attimo dopo, come un liquido versato. Ogni riga ha anche un pezzo gemello: fra due blocchi la riga scelta li raggiunge da tutte e due le parti, e lasciata in mezzo a un blocco si divide nelle sue due metà, che tornano ciascuna verso il proprio vicino. Tornando nella casella il pezzo prima scivola di lato e poi si stringe, per non diventare una riga sottile sopra il nome.
La barra è nel piede della tabella: il riepilogo se ne va e un quadrato scuro si srotola a destra fino alla larghezza di quello che dice, con un piccolo slancio; le sue parti sono tagliate dal bordo che avanza (clip-path) e arrivano una dopo l'altra quando il bordo le passa. Il numero delle righe scelte rotola, la somma conta, e Mark as paid si spegne quando nessuna riga scelta è da incassare. Le azioni che si possono annullare cambiano la barra nel messaggio con Undo e una linea che si accorcia in UNDO_FOR secondi; il fuoco passa sempre al pulsante giusto prima che il vecchio sparisca.
Delete prende ogni gruppo di righe vicine e ne fa una porta: una copia delle righe, con le stesse larghezze di colonna, sotto la tabella, nello stesso punto. Le righe vere escono subito dal DOM e quelle di sotto vengono riportate dove erano con una trasformazione (FLIP). La porta gira attorno al bordo di sopra con perspective e rotateX(−φ), spinta dalla gravità, φ'' = g·cos φ, con g calcolata perché sia di taglio dopo DROP secondi, e si scurisce col seno dell'angolo. Quando la porta è a 35 gradi le righe di sotto partono tutte insieme sulla stessa molla, ognuna per la sua strada: così ogni buco si chiude in proporzione e la tabella non si strappa dove non c'era un buco. Il buio fra le righe è ridisegnato a ogni fotogramma in SVG, un rettangolo per ogni spazio, più scuro sotto il bordo di sopra, e niente si vede sotto il piede.
Le righe tornano al loro posto nel DOM ma nascoste; quelle di sotto partono dal posto di prima e scendono a fare spazio, aprendo i buchi scuri, e dentro ogni buco la sua porta risale da di taglio con una spinta costante, sbatte in piano e rimbalza una volta (restituzione 0,26). Quando si ferma, le righe vere prendono il suo posto con la loro forma già intera, scelte com'erano, e la barra torna alle azioni. Annullando Mark as paid gli stati rotolano indietro e la scelta ritorna.
È una table con le intestazioni, le caselle sono input veri con l'etichetta nascosta (Select INV-2042, Mirabel Studio), la casella in testa ha lo stato indeterminate, il dettaglio è una riga con aria-controls e aria-expanded, e una regione aria-live dice quante fatture sono scelte, cancellate o segnate. Esc toglie la scelta. Trascinare sulle caselle trattiene il puntatore solo quando arriva su un'altra riga, e il clic che chiude il gesto viene scartato. Sul telefono Invoice e Due passano sotto il nome, il carattere si stringe con l'asse della larghezza (90%) e la cella del dettaglio copre esattamente le colonne rimaste. Un clic da script (l'anteprima della libreria) sceglie, cancella, annulla, sceglie tutto, segna pagato, apre una riga ed esporta, una cosa per clic.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
INVOICES | script.js | [['Halden Moss', 'INV-2041', 'Sep 30', 2400, 'paid'], …] | Le fatture: cliente, numero, scadenza, importo in euro e stato (paid, overdue, sent o draft). |
DETAILS | script.js | [['Brand guidelines, second round', 'Ines Halden', 'Art direction, 3 days', 1500, 'Type licences', 'Sent Sep 16|Opened Sep 17|Paid Sep 29'], …] | Il dettaglio di ogni fattura quando la sua riga si apre: progetto, persona, una prima voce col suo importo, una seconda voce (il resto dell'importo) e quello che è successo, diviso da |. |
SPRING | script.js | [360, 0.56] | Le molle della forma della scelta: quanto sono rigide, e quanto smorzate, da 0 (ondeggia tanto) a 1 (non ondeggia). |
RADIUS | script.js | 12 | Il raggio degli angoli della forma, in pixel. |
DROP | script.js | 0.38 | I secondi che le righe cancellate impiegano a cadere nella botola. |
CLOSE | script.js | [320, 0.78] | Le molle delle righe che chiudono i buchi: quanto sono rigide, e quanto smorzate. |
CHAIN | script.js | 0.05 | I secondi fra una botola e la successiva, quando si cancellano righe di punti diversi. |
UNDO_FOR | script.js | 6 | I secondi in cui Undo resta nella barra. |
Per le fatture di uno studio, gli ordini di un negozio, i membri di un gruppo, le iscrizioni di una newsletter, i file di un archivio: ogni gestionale dove si scelgono delle righe per farci qualcosa tutte insieme.
A 390 pixel le colonne Invoice e Due scompaiono e il numero e la scadenza passano sotto il nome del cliente; il carattere si stringe al 90% con l'asse della larghezza, le righe sono alte 58 pixel e le caselle sono bersagli di 44. Nella barra restano le icone (le parole restano per gli screen reader). Trascinare il dito lungo le caselle sceglie tante righe; altrove il dito fa scorrere la pagina.
Niente cresce né cade: la forma di ogni riga scelta compare e scompare sfumando, già al suo posto, le righe cancellate svaniscono e le altre sono subito al loro posto, quelle riprese ricompaiono sfumando, la barra e il riepilogo si dissolvono l'una nell'altro e il dettaglio si apre già aperto.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 23 KB; le librerie: GSAP 28 KB.
Provata il 6 ottobre 2026
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.
Con l’email e la password del tuo account.
oppure con l’email
Hai fatto l’accesso come .
Con l’account scarichi lo zip di ogni animazione e di ogni pacchetto, gratis.