
Il rullo delle ore
Selettori, 3D, Fisica
NuovaGratis
La barra in basso dell'app di un registratore, fatta come la fila di tasti di un vecchio mangianastri: cinque tasti lunghi color crema col frontalino d'alluminio, e quello della voce scelta premuto e agganciato giù. Premendone un altro, nella fessura sotto i tasti si vede la barretta d'acciaio dell'aggancio che scorre di lato: libera il vecchio tasto, che risale di scatto con un rimbalzo, e si incastra nella tacca del nuovo.
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 interlocking keys tab bar</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the tab bar of a voice recorder app, built like the row of keys of an old tape deck: five long piano keys of
cream plastic with an aluminium front, on a brushed metal plate. The tab you are on is held down by a steel
latch bar, seen through the slot under the keys. Pressing another key, its lug pushes the bar aside, the bar
lets the old key go (it springs back up), then catches the new one in its notch. The keys, the bar and its
spring are worked out by the script on every frame -->
<nav class="deck" aria-label="Main" data-deck>
<span class="deck__well" aria-hidden="true"></span>
<span class="deck__slot" aria-hidden="true">
<svg class="deck__works" data-works></svg>
</span>
<div class="deck__keys">
<button class="key" type="button" data-key data-autoclic>
<span class="key__body" data-body>
<span class="key__top" data-top>
<svg class="key__icon" viewBox="0 0 24 24" aria-hidden="true">
<rect class="key__fill" x="8.6" y="3.2" width="6.8" height="11.2" rx="3.4"/>
<rect x="8.6" y="3.2" width="6.8" height="11.2" rx="3.4"/>
<path d="M5.6 11.2a6.4 6.4 0 0 0 12.8 0M12 17.6v3.2M8.8 20.8h6.4M10.8 7h2.4M10.8 9.6h2.4"/>
</svg>
<span class="key__shade" data-shade></span>
</span>
<span class="key__front" data-front>
<span class="key__name">Record</span>
<span class="key__gloss" data-gloss></span>
</span>
</span>
</button>
<button class="key" type="button" data-key>
<span class="key__body" data-body>
<span class="key__top" data-top>
<svg class="key__icon" viewBox="0 0 24 24" aria-hidden="true">
<path class="key__fill" d="M3.6 6.4h16.8v11.2H3.6z"/>
<rect x="3.2" y="5.6" width="17.6" height="12.8" rx="1.8"/>
<path d="M6.2 18.4l1.6-3.4h8.4l1.6 3.4M7.4 10.4h9.2"/>
<rect x="6.6" y="8.6" width="3.6" height="3.6" rx="1.8"/>
<rect x="13.8" y="8.6" width="3.6" height="3.6" rx="1.8"/>
</svg>
<span class="key__shade" data-shade></span>
</span>
<span class="key__front" data-front>
<span class="key__name">Library</span>
<span class="key__gloss" data-gloss></span>
</span>
</span>
</button>
<button class="key" type="button" data-key data-autoclic>
<span class="key__body" data-body>
<span class="key__top" data-top>
<svg class="key__icon" viewBox="0 0 24 24" aria-hidden="true">
<path class="key__fill" d="M13.6 3.6h3.2v16.8h-3.2z"/>
<path d="M3.6 10.4v3.2M6.8 7.6v8.8M10 5.2v13.6M18.8 9.2v5.6M21.2 11v2"/>
<path d="M13.6 3.6h3.2v16.8h-3.2zM15.2 3.6V2.4M15.2 21.6v-1.2"/>
</svg>
<span class="key__shade" data-shade></span>
</span>
<span class="key__front" data-front>
<span class="key__name">Edit</span>
<span class="key__gloss" data-gloss></span>
</span>
</span>
</button>
<button class="key" type="button" data-key>
<span class="key__body" data-body>
<span class="key__top" data-top>
<svg class="key__icon" viewBox="0 0 24 24" aria-hidden="true">
<path class="key__fill" d="M5.6 10.4h12.8v9.6H5.6z"/>
<path d="M8.8 10.4H6.4a.8.8 0 0 0-.8.8v8.4a.8.8 0 0 0 .8.8h11.2a.8.8 0 0 0 .8-.8v-8.4a.8.8 0 0 0-.8-.8h-2.4"/>
<path d="M12 14.4V3.2M8.4 6.4 12 2.8l3.6 3.6"/>
</svg>
<span class="key__shade" data-shade></span>
</span>
<span class="key__front" data-front>
<span class="key__name">Share</span>
<span class="key__gloss" data-gloss></span>
</span>
</span>
</button>
<button class="key" type="button" data-key data-autoclic>
<span class="key__body" data-body>
<span class="key__top" data-top>
<svg class="key__icon" viewBox="0 0 24 24" aria-hidden="true">
<rect class="key__fill" x="4.4" y="12.2" width="3.2" height="4.4" rx="0.8"/>
<rect class="key__fill" x="10.4" y="6.2" width="3.2" height="4.4" rx="0.8"/>
<rect class="key__fill" x="16.4" y="10.2" width="3.2" height="4.4" rx="0.8"/>
<path d="M6 3.2v9M6 16.6v4.2M12 3.2v3M12 10.6v10.2M18 3.2v7M18 14.6v6.2"/>
<rect x="4.4" y="12.2" width="3.2" height="4.4" rx="0.8"/>
<rect x="10.4" y="6.2" width="3.2" height="4.4" rx="0.8"/>
<rect x="16.4" y="10.2" width="3.2" height="4.4" rx="0.8"/>
</svg>
<span class="key__shade" data-shade></span>
</span>
<span class="key__front" data-front>
<span class="key__name">Settings</span>
<span class="key__gloss" data-gloss></span>
</span>
</span>
</button>
</div>
<span class="deck__lip" aria-hidden="true"></span>
</nav>
</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.
32 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 barra dei tasti a incastro» da Animorio e adattala al mio sitoDi ogni tasto si calcola una sola cosa: quanto è scesa la punta della sua linguetta, in pixel, con la sua velocità. Una molla già un poco compressa la spinge su (SPRING), il dito la spinge giù con una forza costante (FORCE) finché è sopra, un fermo la tiene in alto e uno in fondo. Contro il fermo alto il tasto liberato rimbalza (BOUNCE) e riceve un colpo che lo fa vibrare: una piccola rotazione su una molla rapida e smorzata (SHIVER).
La barretta è un corpo a sé, con la sua massa (HEAVY) e la sua molla (BAR) che la tira contro il fermo. Il becco inclinato della linguetta e il dente della barretta sono un vincolo fra i due: la barretta deve stare spostata almeno quanto dice la profondità della linguetta. Quando non lo è, la correzione si divide fra tasto e barretta secondo le masse e la pendenza del becco (come nei vincoli della dinamica basata sulle posizioni): il tasto rallenta, la barretta parte, e la velocità di tutti e due è lo spostamento vero diviso il tempo.
Nessuna sequenza è scritta: un tasto agganciato si libera quando la barretta si sposta più di quanto il dente entra nella sua tacca; un tasto si aggancia quando la sua tacca è arrivata all'altezza del dente e la barretta è tornata. Così premi, la barretta scorre, il vecchio si libera e il nuovo si incastra succedono da soli, in quell'ordine, e con le manopole cambiano i tempi ma non l'ordine. Il dito lasciato si stacca solo quando il tasto è agganciato: anche un tocco velocissimo arriva in fondo.
Ogni tasto è una faccia di sopra e un frontalino piegato di 50 gradi, in un contesto 3D: perspective, rotateX per guardarlo dall'alto, poi una rotazione attorno a un perno 40 pixel dietro la faccia, proporzionale alla profondità (TILT). La luce viene da una lampada sopra e un poco dietro: quanto ogni faccia è girata verso di lei decide l'ombra sulla plastica e la luminosità dell'alluminio, e il riflesso del frontalino scorre. La fessura è un SVG: la barretta coi denti e la molla a zig-zag, che si allunga, ridisegnate a ogni fotogramma.
Passi di calcolo di al massimo 1/240 di secondo, fotogrammi di al massimo 1/30, quattro giri di vincoli per passo. Un tasto veloce che salterebbe il becco in un solo passo viene rimesso sul becco. Quando niente si muove più il ciclo smette di calcolare e di disegnare. Le voci sono button in un nav, quella scelta ha aria-current="page" appena la si preme.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
START | script.js | 1 | Il tasto premuto all'apertura della pagina (0 è il primo). |
FORCE | script.js | 26000 | Quanto forte il dito preme un tasto: di più, scende più in fretta e spinge via la barretta con più slancio. |
SPRING | script.js | 320 | Quanto è dura la molla di ogni tasto: di più, il tasto liberato risale più di scatto. |
BAR | script.js | 600 | Quanto è dura la molla della barretta: di più, torna indietro e aggancia più in fretta, ma il tasto fa più fatica a spingerla. |
HEAVY | script.js | 2.5 | Quanto pesa la barretta rispetto a un tasto: di più, si muove più piano e oppone più resistenza. |
BOUNCE | script.js | 0.32 | Quanto rimbalza contro il fermo il tasto che risale (0 niente). |
SHIVER | script.js | 1 | Quanto vibra un tasto quando sbatte contro un fermo (0 niente). |
TILT | script.js | 11 | Di quanti gradi si inclina un tasto in fondo alla corsa. |
HOVER | script.js | 0.1 | Quanto scende un tasto sotto il mouse, come parte della sua corsa. |
Per la barra in basso di un'app pensata per il telefono, con quattro o cinque sezioni, quando l'app ha a che fare col suono o con gli oggetti di una volta: un registratore, un'app di podcast, un lettore di musica, un'app per la radio o per il nastro. Il tasto giù dice dove si è anche da fermo.
La placca è larga al massimo 420 pixel e sul telefono si stringe allo schermo con 16 pixel di margine per lato (358 su un telefono di 390); resta alta 72 e ogni tasto è un bersaglio di circa 68 per 72 pixel, con tutti e cinque i nomi interi. Il tocco preme il tasto fino in fondo anche se è velocissimo; senza mouse non c'è l'abbassamento di un capello. In verticale la pagina scorre ancora (touch-action: pan-y).
Niente corsa, niente barretta che scorre, niente rimbalzi né vibrazioni: le facce del vecchio tasto e del nuovo si sbiadiscono un attimo e ricompaiono col nuovo giù e agganciato, icona accesa, e il vecchio su. All'arrivo la placca compare solo in dissolvenza, col tasto già giù.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 11 KB; le librerie: GSAP 28 KB.
Provata il 8 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.