
Il menu con la riga di lettura
Menu, Testo, Hover, Transizioni
NuovaGratis
Il pulsante Share di una playlist: il suo pannello esce dal pulsante quadretto per quadretto, come da una fontana, e ha gli stessi quadretti di un codice QR. Scegliendo QR code il pannello gira i suoi quadretti nel codice vero del link, nell'ordine in cui lo legge un telefono, mentre le lettere del link si accendono; l'evidenziatore è fatto di colonne che si piegano a gradini, e Copy manda una testina gialla lungo il link.
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 share menu made of modules</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/recursive@5.3.0/mono.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the share button of a playlist and the room above it for its panel. The panel is drawn square by square in
the canvas: it comes out of the button, and its squares are the same size as the squares of the QR code it
can turn into -->
<div class="share" data-share>
<div class="share__shade" aria-hidden="true" data-shade></div>
<canvas class="share__canvas" aria-hidden="true" data-canvas></canvas>
<div class="sheet" data-sheet inert>
<!-- the first page: what is shared, where it can go, and its link -->
<section class="sheet__page" data-page="menu">
<div class="sheet__head" data-row>
<svg class="sheet__cover" viewBox="0 0 44 44" aria-hidden="true">
<rect width="44" height="44" fill="#123f49"/>
<path d="M17.5 27a10 10 0 0 1 20 0z" fill="#ffd36e"/><path d="M14 31.5h27" stroke="#ffd36e" stroke-width="1.4" stroke-linecap="round" opacity=".55"/>
<path d="M0 26.5q5.5-3 11 0t11 0 11 0 11 0V44H0z" fill="#1d6772"/>
<path d="M0 31.5q5.5-3 11 0t11 0 11 0 11 0V44H0z" fill="#3b9693"/>
<path d="M0 36.5q5.5-3 11 0t11 0 11 0 11 0V44H0z" fill="#a7d9c8"/>
</svg>
<span class="sheet__what">
<span class="sheet__title" data-title>Slow Tide</span>
<span class="sheet__meta">Playlist · 24 songs</span>
</span>
</div>
<div class="sheet__rule" aria-hidden="true" data-row></div>
<div class="menu" id="share-menu" role="menu" aria-label="Share Slow Tide" tabindex="-1" data-menu>
<a class="item" role="menuitem" tabindex="-1" href="#" data-item="messages" data-row>
<span class="item__face">
<svg class="item__icon" viewBox="0 0 24 24" aria-hidden="true"><path data-bubble d="M12 4.6c4.6 0 8.2 2.9 8.2 6.7S16.6 18 12 18c-1 0-2-.13-2.9-.4L5.2 19.6l1.15-3.35C4.8 15 3.8 13.3 3.8 11.3 3.8 7.5 7.4 4.6 12 4.6z"/></svg>
<span class="item__label">Messages</span>
<span class="item__end"></span>
</span>
</a>
<a class="item" role="menuitem" tabindex="-1" href="#" data-item="mail" data-row>
<span class="item__face">
<svg class="item__icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="3.6" y="5.6" width="16.8" height="12.8" rx="2.6"/><path data-flap d="M4.8 7.2 L12 12.6 L19.2 7.2"/></svg>
<span class="item__label">Mail</span>
<span class="item__end"></span>
</span>
</a>
<div class="item" role="menuitem" tabindex="-1" aria-haspopup="true" data-item="qr" data-row>
<span class="item__face">
<svg class="item__icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="4" y="4" width="6" height="6" rx="1.4"/><rect x="14" y="4" width="6" height="6" rx="1.4"/><rect x="4" y="14" width="6" height="6" rx="1.4"/><g class="item__bits" data-bits><rect x="14" y="14" width="2.4" height="2.4" rx=".5"/><rect x="17.6" y="17.6" width="2.4" height="2.4" rx=".5"/><rect x="17.6" y="14" width="2.4" height="2.4" rx=".5"/></g></svg>
<span class="item__label">QR code</span>
<span class="item__end"><svg class="item__chev" viewBox="0 0 24 24" aria-hidden="true" data-chev><path d="M9.5 6.5 15 12l-5.5 5.5"/></svg></span>
</span>
</div>
<div class="item" role="menuitem" tabindex="-1" data-item="embed" data-row>
<span class="item__face">
<span class="item__glyph"><svg class="item__icon" viewBox="0 0 24 24" aria-hidden="true" data-glyph><path data-left d="M8.4 7.6 4 12l4.4 4.4"/><path data-right d="M15.6 7.6 20 12l-4.4 4.4"/><path data-slash d="M13.4 5.8l-2.8 12.4"/></svg><svg class="item__icon item__done" viewBox="0 0 24 24" aria-hidden="true"><path data-tick d="M5 12.6l4.4 4.4L19 7.4"/></svg></span>
<span class="item__label">Embed</span>
<span class="item__end">
<span class="act" data-act>
<span class="act__words"><span class="act__word" data-word="idle">Copy code</span><span class="act__word" data-word="done">Copied</span></span>
</span>
</span>
</span>
</div>
<div class="sheet__rule" role="separator" data-row></div>
<div class="item item--link" role="menuitem" tabindex="-1" data-item="copy" data-row>
<span class="item__face">
<span class="item__glyph"><svg class="item__icon" viewBox="0 0 24 24" aria-hidden="true" data-glyph><path data-half="a" d="M10.6 13.4a3.6 3.6 0 0 0 5.1 0l3-3a3.6 3.6 0 0 0-5.1-5.1l-1.1 1.1"/><path data-half="b" d="M13.4 10.6a3.6 3.6 0 0 0-5.1 0l-3 3a3.6 3.6 0 0 0 5.1 5.1l1.1-1.1"/></svg><svg class="item__icon item__done" viewBox="0 0 24 24" aria-hidden="true"><path data-tick d="M5 12.6l4.4 4.4L19 7.4"/></svg></span>
<span class="item__url" data-url></span>
<span class="item__end">
<span class="act" data-act>
<span class="act__words"><span class="act__word" data-word="idle">Copy</span><span class="act__word" data-word="done">Copied</span></span>
</span>
</span>
</span>
</div>
</div>
</section>
<!-- the second page: the panel turns its own squares into the QR code of the link -->
<section class="sheet__page sheet__page--qr" aria-label="QR code" data-page="qr" hidden>
<div class="qr__bar">
<button class="qr__btn" type="button" aria-label="Back to the share options" data-back>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 6.5 9 12l5.5 5.5"/></svg>
</button>
<span class="qr__title">Scan to listen</span>
<button class="qr__btn" type="button" aria-label="Save the QR code as an image" data-save>
<svg viewBox="0 0 24 24" aria-hidden="true"><path data-down d="M12 4.5v10M8 10.6l4 4 4-4"/><path d="M5 15.5v2.2A1.8 1.8 0 0 0 6.8 19.5h10.4a1.8 1.8 0 0 0 1.8-1.8v-2.2"/></svg>
</button>
</div>
<div class="qr__code" role="img" data-code></div>
<p class="qr__link" aria-hidden="true" data-caption></p>
</section>
</div>
<button class="trigger" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="share-menu" data-trigger data-autoclic>
<span class="trigger__pill" data-pill>
<svg class="trigger__icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M8.6 9.8H7.2a1.9 1.9 0 0 0-1.9 1.9v6.4A1.9 1.9 0 0 0 7.2 20h9.6a1.9 1.9 0 0 0 1.9-1.9v-6.4a1.9 1.9 0 0 0-1.9-1.9h-1.4"/>
<g data-arrow><path d="M12 14.6V3.8"/><path d="M8.6 7.2 12 3.8l3.4 3.4"/></g>
</svg>
<span class="trigger__label" data-label>Share</span>
</span>
</button>
</div>
</main>
<p class="sr" aria-live="polite" data-say></p>
<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 «Il menu di condivisione a moduli» da Animorio e adattala al mio sitoGSAP porta un numero, fly.p, da 0 a 1 in POUR secondi, e ogni quadretto del pannello ne legge il suo momento: il ritardo è 0,64 volte la sua riga contata dal pulsante, più 0,3 volte il quadrato della sua distanza dal centro, più un poco di suo, e così il pannello cresce a cupola. Ogni quadretto passa in volo il 30% dell'apertura, su una curva di Bézier quadratica da un punto dietro il pulsante al suo posto, col punto di controllo spostato verso l'alto quanto dice FOUNTAIN; cresce dal 45% della misura, si raddrizza e si gonfia un poco atterrando. Quelli posati si disegnano dentro la forma arrotondata del pannello (clip), quelli appena posati restano chiari per 35 millesimi. Alla chiusura lo stesso numero torna a 0 in RETURN secondi: partono prima la cima e i lati, che accelerano verso il pulsante.
Il codice si calcola nello script dal testo di LINK, senza librerie: modo byte, correzione M, versioni da 1 a 10. I bit (modo, lunghezza, i byte, la fine e il riempimento EC 11) diventano parole, ogni blocco riceve la sua correzione di Reed-Solomon sul campo GF(256) col polinomio 0x11D, i blocchi si intrecciano; poi i motivi fissi (i tre angoli, le righe di temporizzazione, i quadrati di allineamento, il formato e la versione), i dati a zig-zag due colonne alla volta dal basso a destra, e le otto maschere provate con le quattro penalità, tenendo la migliore. Il risultato è stato confrontato quadretto per quadretto con la libreria qrcode e letto dalle foto con jsQR.
Il pannello è largo 43 quadretti da 8 pixel e il codice, col suo bordo chiaro di 3 quadretti, comincia su un multiplo di 8: ogni quadretto del codice è un quadretto del pannello. Ognuno si gira come una stecca, con l'altezza moltiplicata per il valore assoluto di cos(πu), in FLIP secondi, dal colore del pannello al suo: prima il bordo e i motivi fissi in un'onda diagonale, poi i dati nell'ordine in cui sono posati, con la testa gialla. Ogni lettera del link sa quali quadretti portano il suo byte (le parole del codice ricondotte indietro attraverso l'intreccio dei blocchi): si accende quando vengono scritti e il suo asse MONO del carattere Recursive va a 1.
L'evidenziatore sono 41 colonne da 8 pixel, ognuna con la sua altezza e il suo posto. Verso una voce nuova ogni colonna parte dopo LAG secondi moltiplicati per la sua distanza dal puntatore e arriva in GLIDE secondi con back.out; da tastiera parte da sinistra. Ogni voce ha sopra una copia scura di sé tagliata da un clip-path polygon() che segue i gradini delle colonne; le parole della faccia hanno lo stesso colore dell'evidenziatore, così sotto di lui spariscono del tutto e si vede solo la copia scura, che non ha bisogno di uno sfondo: sotto, il canvas può disegnare la testina.
Copiando, una testina gialla corre lungo il link in READ secondi: colonne gialle con una scia che si spegne in modo esponenziale, e le lettere a meno di 46 pixel dalla testina prendono l'asse MONO e più peso con font-variation-settings. Poi l'icona gira e si rimpicciolisce, una spunta si disegna al suo posto (stroke-dashoffset) e Copy rotola in DONE con una sfocatura; dopo HOLD secondi torna tutto. Solo una pressione vera scrive negli appunti (navigator.clipboard, e se manca execCommand): il clic dell'anteprima del catalogo non li tocca mai.
Il pulsante ha aria-haspopup, aria-expanded e aria-controls; il pannello ha role menu e le voci menuitem. Frecce, Home, End, la prima lettera di una voce, Invio e Spazio, Esc (dalla pagina del QR prima torna indietro) e Tab, che chiude; il fuoco torna al pulsante a chi usa la tastiera. Messages e Mail sono link veri, sms: e mailto: col titolo e il link. La pagina del QR ha Indietro e Salva, e il Tab gira fra i due; Salva crea un PNG nero su bianco col bordo di 4 quadretti, pronto da stampare.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
LINK | script.js | https://ondine.example/s/slow-tide | Il link che si copia e che diventa il codice QR (fino a 213 byte: uno più lungo si taglia). |
POUR | script.js | 0.62 | I secondi del pannello che esce dal pulsante, quadretto per quadretto. |
FOUNTAIN | script.js | 0.6 | Quanto i quadretti salgono prima di allargarsi: 0 in linea retta, 1 prima dritti in su. |
RETURN | script.js | 0.42 | I secondi del pannello che torna nel pulsante. |
LAG | script.js | 0.12 | Quanto resta indietro la parte lontana dell'evidenziatore, in secondi: 0 lo fa muovere tutto insieme. |
GLIDE | script.js | 0.26 | I secondi che ogni colonna dell'evidenziatore impiega ad arrivare su una voce nuova. |
WEAVE | script.js | 1.3 | I secondi del codice QR che si scrive, quadretto per quadretto. |
READ | script.js | 0.6 | I secondi della testina gialla che corre lungo il link quando si copia. |
DONE | script.js | Copied | La parola della conferma. |
HOLD | script.js | 1.6 | I secondi che la conferma resta prima di tornare a Copy. |
Per il pulsante Condividi di una cosa che la gente si passa: una playlist, un articolo, una ricetta, un evento, un prodotto. Il codice QR serve davvero quando chi guarda è al computer e la cosa gli serve sul telefono: la ricetta in cucina, il biglietto alla porta, la playlist in macchina. L'apertura è fatta per farsi notare; in un'app usata tutto il giorno conviene accorciare POUR e RETURN. Il codice si legge se --ink e --paper restano ben diversi: un colore scuro e uno chiaro.
Il pannello è largo 344 pixel e su uno schermo di 390 ci sta con il suo margine; su uno più stretto di 368 tutto si rimpicciolisce insieme. Si apre e si sceglie col tocco: il dito accende la voce prima che il tocco la scelga. Le voci sono alte 44 pixel, il pulsante 60. Il codice QR mostrato su un telefono si legge con la fotocamera di un altro.
Niente quadretti in volo: il pannello compare al suo posto in dissolvenza e se ne va allo stesso modo. L'evidenziatore salta da una voce all'altra senza colonne in ritardo e le icone stanno ferme. Il codice QR compare tutto insieme in dissolvenza; copiando non c'è la testina né l'onda di lettere: Copy diventa Copied in dissolvenza e l'icona lascia il posto alla spunta.
GSAP 3.15.0, Recursive (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 22 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.