
Il menu sotto il foglio
Menu, 3D, Fisica, Transizioni
NuovaGratis
La ricerca di un negozio di dischi: la lente è un piccolo giradischi che gira a ogni lettera, e i risultati sono le buste in una cassa di legno. Quelle che non c’entrano affondano, le altre si stringono, e la mano le sfoglia in avanti una dopo l’altra, ognuna che rimbalza quando cade. Il disco scelto esce dalla busta e vola sul giradischi; se non c’è niente il disco si graffia e nella cassa si alza un divisorio.
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 record crate search</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the search of a record shop. The lens is a small turntable: typing nudges the record round and the arm
comes over it. The results are the sleeves in a crate: the ones that do not match sink, the others close up,
and the hand flips through them; the record picked slides out of its sleeve and lands on the turntable.
The sleeves are made by the script from the list at the top of script.js -->
<div class="dig" data-dig data-autoclic>
<form class="dig__form" role="search" action="#" novalidate data-form>
<div class="dig__field" data-field>
<svg class="deck" viewBox="0 0 44 44" aria-hidden="true" data-deck>
<rect class="deck__plinth" x="1.5" y="4.5" width="41" height="35" rx="6"/>
<g data-platter>
<circle class="deck__vinyl" cx="18" cy="22" r="14"/>
<circle class="deck__groove" cx="18" cy="22" r="12.4"/>
<circle class="deck__groove" cx="18" cy="22" r="10.6"/>
<circle class="deck__groove" cx="18" cy="22" r="8.8"/>
<circle class="deck__label" cx="18" cy="22" r="5" data-label/>
<path class="deck__half" d="M18 17a5 5 0 0 1 0 10z" data-half/>
</g>
<path class="deck__shine" d="M18 8a14 14 0 0 1 12.1 7l-3.4 2A10 10 0 0 0 18 12z"/>
<circle class="deck__spindle" cx="18" cy="22" r="0.9"/>
<g data-arm>
<circle class="deck__pivot" cx="36" cy="10" r="2.6"/>
<path class="deck__arm" d="M36 10 L36.5 26 L32.6 31.4"/>
<rect class="deck__head" x="30.4" y="30.2" width="4.2" height="2.6" rx="0.6" transform="rotate(-36 32.5 31.5)"/>
</g>
</svg>
<input class="dig__input" id="dig-input" type="text" inputmode="search" enterkeyhint="search" role="combobox" aria-label="Search the records" aria-autocomplete="list" aria-expanded="true" aria-controls="dig-crate" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" placeholder="Search the crates" data-input>
<span class="dig__caret" aria-hidden="true" data-caret></span>
<button class="dig__clear" type="button" aria-label="Clear the search" tabindex="-1" data-clear>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7.5 7.5l9 9M16.5 7.5l-9 9"/></svg>
</button>
</div>
</form>
<div class="dig__shop">
<div class="crate" data-crate>
<div class="crate__bin" id="dig-crate" role="listbox" aria-label="Records" data-bin>
<span class="crate__side crate__side--l" aria-hidden="true"></span>
<span class="crate__side crate__side--r" aria-hidden="true"></span>
<div class="divider" aria-hidden="true" data-divider>
<span class="divider__tab" data-divider-tab></span>
<span class="divider__card">
<span class="divider__small">Nothing under</span>
<span class="divider__word" data-divider-word></span>
</span>
</div>
</div>
<div class="crate__front" aria-hidden="true">
<span class="crate__holder"><span class="crate__tag" data-tag>New in</span></span>
</div>
</div>
<div class="info" data-info>
<div class="info__record" data-record>
<p class="info__meta" data-meta></p>
<p class="info__title" data-title></p>
<p class="info__artist" data-artist></p>
<p class="info__price" data-price></p>
</div>
<div class="info__none" data-none hidden>
<p class="info__try">Try</p>
<div class="info__tries" data-tries></div>
</div>
<button class="listen" type="button" data-listen>
<span class="listen__icon" aria-hidden="true">
<svg class="listen__play" viewBox="0 0 20 20"><path d="M6.5 4.6v10.8a.8.8 0 0 0 1.2.7l8.6-5.4a.8.8 0 0 0 0-1.4L7.7 3.9a.8.8 0 0 0-1.2.7z"/></svg>
<span class="listen__bars" data-bars><i></i><i></i><i></i></span>
</span>
<span class="listen__words" data-listen-words>Listen</span>
</button>
</div>
</div>
<div class="dig__flight" aria-hidden="true" data-flight></div>
<p class="sr" role="status" aria-live="polite" data-said></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>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.
207 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 ricerca nella cassa dei dischi» da Animorio e adattala al mio sitoOgni busta è un div con transform-style preserve-3d e il perno in basso (transform-origin 50% 100%), dentro un contenitore con perspective 900px e il punto di vista sopra la cassa (perspective-origin 50% -40%). Ha tre facce: la copertina un pixel davanti (translateZ), il retro girato con rotateX(180deg) un pixel dietro, e il disco in mezzo, così il disco sta dentro la busta per davvero e ne esce senza passarle sopra. Le copertine sono SVG disegnati dallo script coi colori di RECORDS; il davanti della cassa e il portaetichette stanno sopra a tutto, e il contenitore taglia solo il fondo, dove le buste affondano.
c dice quanto ha sfogliato la mano: la busta davanti, con le cadute in mezzo. Ogni busta conosce la sua distanza d da c: dietro (d maggiore di zero) si inclina di LEAN gradi, sale di STEP pixel per posto e va indietro di GAP; fra 0 e -1 cade in avanti fino a FLIP gradi, con una curva smoothstep. c segue il suo bersaglio su una molla (DIG), e ogni busta segue l’inclinazione che le spetta su una molla sua (BOUNCE), poco smorzata: sfogliando di colpo le buste cadono a cascata e ognuna rimbalza quando atterra. Lo z-index si ricalcola da d a ogni fotogramma, perché le buste cadute stanno davanti a quelle in piedi.
search() confronta le parole senza maiuscole né accenti: prima i titoli che cominciano così, poi quelli con una parola che comincia così, poi artista e genere. Le buste che non c’entrano affondano (sink da 0 a 1, cioè giù della loro altezza dietro il davanti della cassa), quelle che restano prendono il posto nuovo e ci scivolano su una molla, e quelle che tornano risalgono dal fondo già al loro posto, una dopo l’altra con back.out. Il bersaglio di c torna a zero: il risultato migliore sta davanti.
Un trascinamento sulla cassa (pointer capture, touch-action none) muove c direttamente, un posto ogni 44 pixel, con un po’ di elastico oltre i bordi; lasciato, la velocità del dito, misurata e smussata, decide fin dove sfoglia ancora. Un tocco senza spostarsi prende la busta toccata: quella davanti si ascolta, una più indietro viene portata davanti. Anche frecce e rotella spostano il bersaglio di c di un posto. Il giradischi è un SVG: il disco gira con una velocità che ogni lettera aumenta e l’attrito riporta a zero, il braccio gira attorno al suo perno con l’attributo transform.
Il disco esce di lato dalla busta in 0,42 secondi, mentre la busta si inclina verso la mano. Poi se ne misura il riquadro sullo schermo (getBoundingClientRect, già in prospettiva) e al suo posto parte una copia in un livello che sta sopra il campo ma sotto la cassa, così all’inizio è ancora coperta dalla copertina come quello vero. La copia va a sinistra in fretta (power3.out) e sale piano (power2.in), rimpicciolendo subito: gira attorno alla cassa e non passa mai sopra le parole scritte. Arrivata, l’etichetta del giradischi prende il suo colore e il disco accelera fino a RPM giri al minuto.
Tutto quello che si muove, molle, buste, giradischi, si integra a passi di 1/240 di secondo in una sola funzione aggiunta al ticker di GSAP, con il passo limitato a 1/30. Quando le molle sono ferme, non c’è un tween in programma (contando anche quelli che aspettano il loro ritardo) e il disco non suona, la funzione si toglie dal ticker. Un clic da script (detail 0, l’anteprima della libreria) fa un passo alla volta, senza prendere il fuoco e con un cursore disegnato: soul e una busta sfogliata, il disco davanti sul giradischi, polka che non c’è, dub proposto, e tutta la cassa sfogliata avanti e indietro.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
SLEEVE | script.js | 200 | Il lato di una busta, in pixel (su un telefono si rimpicciolisce per starci). |
GAP | script.js | 22 | Quanto stanno lontane le buste nella cassa, in profondità, in pixel. |
STEP | script.js | 22 | Quanto si vede di ogni busta più indietro sopra quella davanti, in pixel: lo spazio per il titolo. |
LEAN | script.js | 12 | Quanto sono inclinate all’indietro le buste in piedi, in gradi. |
FLIP | script.js | 72 | Quanto cade in avanti una busta sfogliata, in gradi. |
DIG | script.js | [240, 27] | La molla della mano che sfoglia: rigidità e smorzamento. |
BOUNCE | script.js | [520, 16] | La molla su cui atterra ogni busta che cade: rigidità e smorzamento (meno smorzamento, più rimbalzo). |
RPM | script.js | 33 | Quanto gira il disco sul giradischi, in giri al minuto. |
TRIES | script.js | ['soul', 'dub', 'jazz'] | Le parole proposte quando nella cassa non c’è niente. |
RECORDS | script.js | [['Night Ferry', 'The Calloway Five', 'Jazz', 1961, '£28', 'rings', …], …] | I dischi della cassa, dai più nuovi: titolo, artista, genere, anno, prezzo, la copertina (stile fra rings, sun, waves, bands, letter, arch, blocks e split, carta, inchiostro, accento) e il colore dell’etichetta. |
Per la ricerca di un negozio di dischi, di un’etichetta, di una radio, di una libreria o di un negozio di giochi da tavolo: ovunque i risultati hanno una copertina e cercarli deve somigliare a frugare fra le cose vere. La funzione search() è la sola da cambiare per chiedere i risultati a un server, e play() è il punto dove far partire l’anteprima audio vera.
Il campo prende la larghezza dello schermo meno 32 pixel e resta alto 60, con le parole di 17 pixel (da 16 in su iPhone non ingrandisce la pagina toccando il campo). La cassa sta sotto il campo, con le buste rimpicciolite a 180 pixel perché ci stiano cassa e titoli, e la descrizione diventa una riga sotto la cassa: titolo e artista a sinistra, Listen a destra. Si sfoglia trascinando il dito in giù sulla cassa, con lo slancio, e si tocca una busta per portarla davanti o per ascoltarla.
Niente cadute, voli né rimbalzi: le buste sono già al loro posto, in piedi o cadute, e quelle che non c’entrano svaniscono in dissolvenza invece di affondare. Il disco non vola: l’etichetta del giradischi cambia colore in dissolvenza e il braccio è già nei solchi, ma il disco non gira e le barrette del pulsante stanno ferme. Niente graffio né tremolio quando nulla corrisponde: il divisorio compare in dissolvenza. La ricerca, la tastiera e il tocco funzionano uguali.
GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 16 KB; le librerie: GSAP 28 KB.
Provata il 13 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.