
Il pulsante a strati
Pulsanti, Hover, Fisica
Gratis
La barra in basso dell'app di un caffè di quartiere: sopra la voce scelta pende una piccola insegna smaltata col suo nome, appesa con due cordini a una barretta d'ottone. Cambiando voce i ganci corrono sulla barretta e l'insegna li segue in ritardo: resta indietro, si inclina, oscilla oltre e si assesta come un corpo rigido vero, poi l'icona sotto fa un salto e le dà un colpetto.
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 shop sign tab bar</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the tab bar of a neighbourhood café's app, on a cream board. Along its top runs a thin brass rail, and from
two hooks on it hangs a small enamel sign with the name of the tab you are on, like the plate over a shop
door. Choosing another tab, the hooks run along the rail and the sign follows on its two cords: it trails,
tilts, swings past and settles, and the icon under it hops. The sign is a rigid plate worked out by the
script on every frame -->
<nav class="bar" aria-label="Main" data-bar>
<button class="tab" type="button" data-tab data-autoclic>
<span class="tab__lift" data-lift>
<svg class="tab__icon" viewBox="0 0 24 24" aria-hidden="true">
<path class="tab__fill" d="M5.4 4.6h13.2l1.6 4.6H3.8z"/>
<path d="M3.8 9.2 5.4 4.6h13.2l1.6 4.6M8.7 4.6l-.8 4.6M12 4.6v4.6M15.3 4.6l.8 4.6"/>
<path d="M3.8 9.2a2.05 2.05 0 0 0 4.1 0a2.05 2.05 0 0 0 4.1 0a2.05 2.05 0 0 0 4.1 0a2.05 2.05 0 0 0 4.1 0"/>
<path d="M5.3 12.4v7.4h13.4v-7.4M10.2 19.8v-4.4h3.6v4.4"/>
</svg>
</span>
<span class="sr" data-name>Home</span>
</button>
<button class="tab" type="button" data-tab>
<span class="tab__lift" data-lift>
<svg class="tab__icon" viewBox="0 0 24 24" aria-hidden="true">
<path class="tab__fill" d="M5.2 10.2h11.2v3a5.2 5.2 0 0 1-5.2 5.2h-.8a5.2 5.2 0 0 1-5.2-5.2z"/>
<path d="M5.2 9.4h11.2v3.8a5.2 5.2 0 0 1-5.2 5.2h-.8a5.2 5.2 0 0 1-5.2-5.2zM16.4 10.6h1.2a2.3 2.3 0 0 1 0 4.6h-1.5M3.8 20.6h14"/>
<path d="M8.9 3.4c-.9 1.1.9 2.1 0 3.4M12.6 3.4c-.9 1.1.9 2.1 0 3.4" data-steam/>
</svg>
</span>
<span class="sr" data-name>Menu</span>
</button>
<button class="tab" type="button" data-tab data-autoclic>
<span class="tab__lift" data-lift>
<svg class="tab__icon" viewBox="0 0 24 24" aria-hidden="true">
<path class="tab__fill" d="M5.6 8.4h12.8l-.9 11.2a1 1 0 0 1-1 .9H7.5a1 1 0 0 1-1-.9z"/>
<path d="M5.6 8.4h12.8l-.9 11.2a1 1 0 0 1-1 .9H7.5a1 1 0 0 1-1-.9zM9.2 10.8V7.4a2.8 2.8 0 0 1 5.6 0v3.4"/>
</svg>
</span>
<span class="sr" data-name>Orders</span>
</button>
<button class="tab" type="button" data-tab>
<span class="tab__lift" data-lift>
<svg class="tab__icon" viewBox="0 0 24 24" aria-hidden="true">
<path class="tab__fill" d="M7 4.4h10a.6.6 0 0 1 .6.6v15.2l-5.6-4-5.6 4V5a.6.6 0 0 1 .6-.6z"/>
<path d="M7 4.4h10a.6.6 0 0 1 .6.6v15.2l-5.6-4-5.6 4V5a.6.6 0 0 1 .6-.6z"/>
</svg>
</span>
<span class="sr" data-name>Saved</span>
</button>
<button class="tab" type="button" data-tab data-autoclic>
<span class="tab__lift" data-lift>
<svg class="tab__icon" viewBox="0 0 24 24" aria-hidden="true">
<path class="tab__fill" d="M12 4.4a3.7 3.7 0 1 0 0 7.4a3.7 3.7 0 1 0 0-7.4z"/>
<path d="M12 4.4a3.7 3.7 0 1 0 0 7.4a3.7 3.7 0 1 0 0-7.4zM4.8 20.2c.9-3.7 3.7-5.7 7.2-5.7s6.3 2 7.2 5.7"/>
</svg>
</span>
<span class="sr" data-name>Profile</span>
</button>
<!-- the brass rail along the top, and everything that hangs from it: the sign's shadow on the board, the two
cords, the two hooks and the sign. None of it takes the taps -->
<span class="rail" aria-hidden="true" data-rail></span>
<span class="hanger" aria-hidden="true" data-hanger>
<span class="shade" data-shade></span>
<svg class="cords">
<g class="cords__shade" transform="translate(1 2.4)"><path data-cord-shade/><path data-cord-shade/></g>
<g class="cords__line"><path data-cord/><path data-cord/></g>
</svg>
<span class="hook" data-hook></span>
<span class="hook" data-hook></span>
<span class="sign" data-sign>
<span class="sign__plate">
<span class="sign__rule"></span>
<span class="sign__words" data-words></span>
<span class="sign__side" data-side></span>
<span class="sign__gloss" data-gloss></span>
</span>
<span class="sign__clip" data-clip></span>
<span class="sign__clip sign__clip--right" data-clip></span>
</span>
</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.
26 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 con l'insegna» da Animorio e adattala al mio sitoL'insegna sono i suoi quattro angoli, quattro punti con posizione e velocità. Sei vincoli di distanza li tengono rigidi: i quattro lati e le due diagonali. A ogni passo di calcolo i punti si muovono con la loro velocità e la gravità, poi i vincoli rimettono a posto le posizioni (sei giri), e la velocità nuova è lo spostamento vero diviso il tempo: l'integrazione di Verlet scritta con le velocità, che accetta i colpi e le spinte. Passi di al massimo 1/240 di secondo, fotogrammi di al massimo 1/30.
Ogni cordino è un vincolo che agisce solo quando è teso: se il punto a cui è legato è più lontano dal gancio della lunghezza del cordino, la lastra viene tirata indietro. Il punto di legatura sta dentro la lastra, quindi è una miscela di tre angoli, e la correzione si divide fra loro secondo il loro peso nella miscela. I cordini sono un poco inclinati verso l'interno (TOE): con cordini paralleli la lastra scivolerebbe dritta come un'altalena, così invece dondola inclinandosi, come un pendolo appeso a un punto sopra la barretta.
I ganci li porta GSAP: una tween sposta il loro punto di mezzo con power2.inOut, e un'altra fa correre quello davanti qualche pixel avanti e poi lo riporta. Dentro il fotogramma i ganci si spostano un poco a ogni passo di calcolo, così anche a 30 fotogrammi al secondo la lastra non riceve strappi. Lo smorzamento agisce sulla velocità della lastra rispetto a quella dei ganci: frena l'oscillazione, non il viaggio.
Girare verso una voce è la torsione di un'insegna appesa a due fili (un pendolo bifilare), con la sua molla: la rigidezza viene dalla gravità, dalla distanza fra i cordini, dalla loro lunghezza e dalla larghezza dell'insegna. L'insegna è un elemento HTML vero (il testo resta nitido) spostato con translate, rotate e perspective più rotateY; i cordini sono due tracciati SVG fino ai fermagli, calcolati con la stessa prospettiva, dritti se tesi e incurvati verso l'esterno se allentati; l'ombra sul fondo segue la lastra.
Quando l'insegna arriva, l'icona sotto salta con una timeline di GSAP; mentre sale diventa un ostacolo che il risolutore conosce: se il bordo basso della lastra le sta dentro, viene spinto su, e la lastra sobbalza da sola. Al contrario, se la lastra oscillando scende su un'icona, l'icona viene spinta giù e piegata dal lato premuto, e torna su una molla. Le voci sono button in un nav, quella scelta ha aria-current="page"; quando niente si muove più il ciclo smette di calcolare e di disegnare.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
START | script.js | 1 | La voce scelta all'apertura della pagina (0 è la prima). |
TRAVEL | script.js | 0.46 | I secondi che i ganci ci mettono ad arrivare alla voce accanto (a una lontana un po' di più). |
LEAD | script.js | 5 | Di quanti pixel il gancio davanti corre avanti a quello dietro durante il viaggio: l'insegna si inclina. |
CORD | script.js | 7 | La lunghezza dei due cordini, in pixel. |
TOE | script.js | 3.5 | Quanto i cordini si inclinano l'uno verso l'altro, in pixel: di più, l'insegna dondola inclinandosi di più (0, scivola dritta). |
GRAVITY | script.js | 2400 | Quanto pesa l'insegna, in pixel al secondo quadrato: di più, oscillazioni più rapide e più piccole. |
DAMPING | script.js | 2.6 | Quanto presto si spegne l'oscillazione: di più, si ferma prima. |
TURN | script.js | 30 | Di quanti gradi l'insegna si gira verso la voce sotto il mouse o sotto il dito. |
HOP | script.js | 6 | Quanto salta l'icona quando l'insegna le arriva sopra, in pixel (sopra i 5 circa le dà un colpetto). |
Per la barra in basso di un'app pensata per il telefono, con quattro o cinque sezioni, quando l'app ha un carattere di bottega: un caffè, una libreria, un forno, un negozio di quartiere, un'app di ricette. L'insegna dice dove si è anche da ferma, al posto delle etichette sotto le icone.
La barra è 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 voce è un bersaglio di circa 69 per 72 pixel. L'insegna è larga 60 pixel e sta dentro la sua colonna anche lì. Col dito giù su una voce l'insegna si gira verso di lei, come col mouse sopra, e il tocco la sceglie. In verticale la pagina scorre ancora (touch-action: pan-y).
Niente viaggio, niente oscillazione, niente torsione e niente salto: l'insegna, coi suoi cordini e i ganci, sparisce in dissolvenza dalla voce vecchia e ricompare ferma e dritta sulla nuova, col nome nuovo, mentre le icone si accendono e si spengono in dissolvenza. All'arrivo la barra compare solo in dissolvenza, con l'insegna già appesa.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 12 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.