
Il menu a onda di peso
Menu, Testo, Hover, Fisica
NuovaGratis
La barra in alto del sito di una riserva naturale, con una coppia di rondini posate sulle lettere della pagina corrente, in cima alle aste come su due pali: i posti si leggono dal contorno vero delle lettere. Puntando un'altra voce una rondine vola a vedere e saltella lungo la parola dietro al puntatore; scegliendola, partono tutte e due, attraversano la barra battendo e planando e scendono sulla parola nuova, e la lettera su cui si posano si abbassa sotto il loro peso.
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 swallows nav</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/albert-sans@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the top bar of a wetland nature reserve. A pair of swallows lives on it, perched on the letters of the page
you are on; pointing at another link sends one of them over to have a look, and choosing it moves them both.
On a phone the links fold into a menu, and the swallows wait on the word Menu -->
<header class="bar" data-bar>
<a class="brand" href="#" aria-label="Fenlark, home">
<svg class="brand__mark" viewBox="0 0 28 28" aria-hidden="true">
<path d="M11.6 26c0-5.4-.2-10.3-.6-14.6M16.6 26c.1-4 .4-7.3 1-10.2M10.9 4.6l-.2-2.4M18.3 9.2l.3-2.1"/>
<path d="M10.4 26c-.9-4.1-2.9-7.4-6.2-9.6M17.8 26c1.1-5.1 3.3-9.4 6.4-12.4"/>
<rect class="brand__head" x="9.3" y="4.4" width="3.2" height="9" rx="1.6"/>
<rect class="brand__head" x="16.5" y="8.9" width="3" height="7.4" rx="1.5" transform="rotate(7 18 12.6)"/>
</svg>
<span class="brand__name">Fenlark</span>
</a>
<!-- on a phone: the button that opens the links; the swallows wait on its word -->
<button class="menu" type="button" aria-expanded="false" aria-controls="fenlark-links" data-menu>
<span class="menu__words">
<span class="menu__word" data-menu-word>Menu</span>
<span class="menu__word menu__word--close" aria-hidden="true" data-close-word>Close</span>
</span>
<svg class="menu__icon" viewBox="0 0 18 18" aria-hidden="true">
<path class="menu__line menu__line--a" d="M3 6.5h12"/>
<path class="menu__line menu__line--b" d="M3 11.5h12"/>
</svg>
</button>
<nav class="nav" id="fenlark-links" aria-label="Main" data-nav>
<ul class="nav__list">
<li><a class="nav__link" href="#" data-link data-autoclic>Visiting</a></li>
<li><a class="nav__link" href="#" aria-current="page" data-link>Wildlife</a></li>
<li><a class="nav__link" href="#" data-link data-autoclic>Trails</a></li>
<li><a class="nav__link" href="#" data-link data-autoclic>Events</a></li>
<li><a class="nav__link" href="#" data-link data-autoclic>Shop</a></li>
</ul>
</nav>
<a class="cta" href="#"><span class="cta__long">Become a member</span><span class="cta__short">Join</span></a>
<!-- the swallows, drawn over the bar and the air above it -->
<canvas class="sky" aria-hidden="true" data-sky></canvas>
</header>
</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.
30 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 nav delle rondini» da Animorio e adattala al mio sitoOgni parola è divisa in lettere, ognuna in un suo inline-block (nascoste a chi legge lo schermo, che sente la parola intera), con in fondo un inline-block senza misura che sta sulla linea di base e dice dov'è. Ogni lettera si disegna da sola su un canvas nascosto, quattro volte più grande, con lo stesso font letto da getComputedStyle (peso variabile compreso), e il suo contorno si legge dall'alto colonna per colonna: per ogni colonna l'altezza del primo pixel con inchiostro sopra la linea di base. Un posto è un tratto in piano (meno di 0,8 pixel di differenza) largo poco più di un pixel, la presa di una rondine: va bene anche la cima di un'asta. Ogni lettera tiene al massimo due posti, i più alti, e i punti di i e j non sono posti.
Un posto vale solo se nessuna lettera vicina, o la parte più alta della stessa lettera, sale oltre il posto entro 0,3 volte la lunghezza della rondine da una parte e dall'altra: così la rondine non sta mai dentro un'asta né sopra il punto di una i. Le rondini si mettono dalla prima lettera all'ultima, distanti almeno 0,72 volte una rondine se si può, scegliendo i posti più alti. Posata, una rondine si gira verso la lettera più alta che ha accanto, così la coda sta sul lato basso e non copre le lettere. I posti si misurano con offsetLeft e offsetTop, che non vedono i transform.
Ogni rondine vola da sola con un comportamento di sterzata alla Reynolds: punta prima la cima di un arco (ARC pixel sopra la più alta delle due parole, meno per un volo corto), poi un punto sopra e dietro il suo posto, poi il posto. Lontana va a SPEED pixel al secondo, si tiene a più di una volta e mezza la sua lunghezza dalle altre e si allinea alla loro velocità entro 60 pixel, con un'accelerazione massima di 5,5 volte SPEED; l'ultimo tratto è un arrivo smorzato in modo critico, senza oltrepassare il posto. Verso una parola molto più in basso (il pannello del telefono) fa una picchiata invece di salire. Le partenze sono sfalsate di 75 millisecondi, prima la più vicina.
La rondine è disegnata su un canvas con curve di Bézier: corpo, testa, petto chiaro, gola ruggine, due penne della coda. Le ali si calcolano in 3D: battono da 1 radiante sopra a 0,75 sotto, tirate indietro tanto più quanto sono alzate, e si guardano di lato e 0,8 radianti dal basso, così ad ali orizzontali quella vicina scende sotto il corpo e quella lontana sale sopra, l'ancora della rondine in volo. Battiti e planate si alternano a ritmo (fra 0,46 e 0,66 secondi), con più planata in discesa e più battiti in salita o in virata; a 30 pixel dal posto le ali si alzano e il muso si solleva per frenare. Un alone sottile del colore della barra la stacca dalle lettere.
Ogni lettera è su una molla (rigidità 520, smorzamento 17) e muove il suo translateY: una rondine che atterra le dà una spinta in giù di DIP per 26 pixel al secondo, più forte quanto più arriva veloce, una che parte una spinta in su, e la rondine sale e scende con lei. Ferme, le rondini si girano col più piccolo dei saltelli verso il puntatore che passa, e ogni RESTLESS secondi circa una si sposta di una lettera (tenendo la distanza), si gira o scuote le ali. Il ciclo sul ticker di GSAP ridisegna solo quando qualcosa si muove.
Dopo 80 millisecondi sopra una voce parte la rondine più vicina e si posa sul posto libero più vicino al puntatore; se il puntatore si allontana di 11 pixel lo segue con un saltello, al massimo ogni 0,26 secondi, e 0,3 secondi dopo che ha lasciato le voci torna al suo posto. Il fuoco da tastiera fa lo stesso, al centro della parola. Le voci sono link in un nav con aria-current. Sotto gli 820 pixel le voci passano in un pannello sotto la barra, aperto dal pulsante Menu (aria-expanded), e le rondini aspettano sulla parola Menu. L'anteprima della libreria clicca le voci a turno, sul telefono anche Menu.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
BIRDS | script.js | 2 | Quante rondini vivono sulla barra: di serie una coppia, ai due capi della parola. |
SPEED | script.js | 420 | Quanto in fretta volano, in pixel al secondo. |
ARC | script.js | 34 | Quanto sale sopra le parole un volo lungo, in pixel. |
SIZE | script.js | 1.2 | Quanto è lunga una rondine, rispetto alla grandezza delle lettere su cui sta. |
DIP | script.js | 2 | Quanto si abbassa una lettera sotto una rondine che le atterra sopra, in pixel. |
SCOUT | script.js | true | Se puntando una voce una rondine ci vola a vedere. |
RESTLESS | script.js | 4 | I secondi, più o meno, fra due piccoli movimenti delle rondini posate (0: stanno ferme). |
Per la barra in alto di un sito che parla di natura e di cose vive: una riserva, un parco, un orto botanico, un rifugio per animali, un'app di birdwatching o di passeggiate, una rivista di viaggi lenti. Le rondini dicono dove sei anche da ferme, e quella che vola a vedere fa capire dove stai per andare.
Sotto gli 820 pixel la barra è alta 58 pixel e larga quanto lo schermo meno 32 (358 a 390): restano il nome, Join al posto di Become a member e il pulsante Menu con le sue due righe, e le rondini aspettano sulla parola Menu. Toccandolo le righe si incrociano in una X, Menu diventa Close e sotto la barra si apre il pannello con le voci a 21 pixel su righe alte 56: le rondini ci scendono in picchiata fino alla pagina corrente, e diventano più grandi con le lettere. Scelta un'altra voce volano lì; chiudendo (Close, Esc o un tocco fuori) tornano su Menu. Il dito giù su una voce manda subito una rondine, il tocco sceglie.
Le rondini non volano e non saltano: spariscono in dissolvenza dalla parola vecchia e ricompaiono già posate sulla nuova. Niente rondine che va a vedere sotto il puntatore, niente giri per guardarlo, niente movimenti a riposo e niente lettere che si abbassano; all'arrivo compaiono sfumando. Sul telefono il pannello compare in dissolvenza e le righe del menu diventano la X senza ruotare.
GSAP 3.15.0, Albert Sans (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 16 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.