
Il menu contestuale a imbuto
Menu, Transizioni, SVG, Hover
NuovaGratis
Gli annunci di una società di corsa, dove i soci possono solo reagire. Ogni reazione è disegnata con una riga sola, come i disegni che i podisti tracciano sulla mappa col percorso. Scelta dalla barra, il disegno si srotola in una linea che fa il giro del messaggio come una traccia GPS, ridisegna la reazione al suo posto sotto di lui e poi si riavvolge dietro di lei come un metro a nastro: la reazione atterra con un sobbalzo e il numero rotola avanti. Toccata sotto il messaggio, fa il giro completo e torna da dove è partita; tolta, si srotola e corre via all'indietro.
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 reactions that run a lap</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 announcements of a running club: a reaction leaves the bar as a line, runs a lap round the message like a
GPS trace and zips into its place under it, drawing itself there; taken back, it unravels and runs off -->
<section class="chat" aria-label="Lockside Run Club, announcements" data-chat data-autoclic>
<header class="chat__head">
<svg class="chat__mark" viewBox="0 0 44 44" aria-hidden="true">
<rect class="mark__track" x="7" y="13.5" width="30" height="17" rx="8.5"/>
<rect class="mark__lane" x="12.5" y="18.5" width="19" height="7" rx="3.5"/>
<path class="mark__line" d="M22 13.5v5"/>
</svg>
<div class="chat__title">
<h2 class="chat__name">Lockside Run Club</h2>
<p class="chat__sub">Announcements · 46 members</p>
</div>
</header>
<div class="chat__thread" data-thread>
<div class="chat__content" data-content>
<ol class="chat__log" data-log>
<li class="msg" data-reactions="kudos 9, love 3" style="--face: #f0915f">
<div class="msg__row">
<span class="msg__face" aria-hidden="true">M</span>
<div class="bubble" data-bubble>
<p class="bubble__who">Maya</p>
<p class="bubble__text">Sunday long run: 18 km from the lock gates, 8:00 sharp.<span class="bubble__room" aria-hidden="true"></span></p>
<span class="bubble__time">07:12</span>
</div>
</div>
<div class="chips" role="group" aria-label="Reactions to Maya’s message" data-chips></div>
</li>
<li class="msg" data-reactions="kudos 6" style="--face: #6aa6ff">
<div class="msg__row">
<span class="msg__face" aria-hidden="true">J</span>
<div class="bubble" data-bubble>
<p class="bubble__who">Jonah</p>
<p class="bubble__text">Track night moves to Thursday, the stadium has a concert.<span class="bubble__room" aria-hidden="true"></span></p>
<span class="bubble__time">08:40</span>
</div>
</div>
<div class="chips" role="group" aria-label="Reactions to Jonah’s message" data-chips></div>
</li>
<li class="msg" data-reactions="fire 7, medal 4" style="--face: #c79bff">
<div class="msg__row">
<span class="msg__face" aria-hidden="true">P</span>
<div class="bubble" data-bubble>
<p class="bubble__who">Priya</p>
<p class="bubble__text">Saturday’s results: Dev ran the 10K in 38:41, a new club record!<span class="bubble__room" aria-hidden="true"></span></p>
<span class="bubble__time">18:05</span>
</div>
</div>
<div class="chips" role="group" aria-label="Reactions to Priya’s message" data-chips></div>
</li>
<li class="msg" data-reactions="haha 12" data-mine="haha" style="--face: #6aa6ff">
<div class="msg__row">
<span class="msg__face" aria-hidden="true">J</span>
<div class="bubble" data-bubble>
<p class="bubble__who">Jonah</p>
<p class="bubble__text">Lost and found: one left shoe, size 42. Smells heroic.<span class="bubble__room" aria-hidden="true"></span></p>
<span class="bubble__time">18:31</span>
</div>
</div>
<div class="chips" role="group" aria-label="Reactions to Jonah’s message" data-chips></div>
</li>
</ol>
<!-- the lines that run, above the messages -->
<svg class="chat__runs" aria-hidden="true" data-runs></svg>
</div>
</div>
<footer class="chat__foot">
<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="10.5" width="14" height="10" rx="2.5"/><path d="M8 10.5V8a4 4 0 0 1 8 0v2.5"/></svg>
<span>Only run leaders can post here</span>
</footer>
<p class="sr" aria-live="polite" data-say></p>
</section>
</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.
79 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 «Le reazioni che fanno il giro» da Animorio e adattala al mio sitoOgni reazione è un solo path SVG in un quadrato di 24 unità, disegnato da noi in modo che cominci e finisca al suo piede, dove la linea entra ed esce: il pollice parte dalla riga fra polsino e mano, il cuore dalla punta, la faccina da un punto in basso a sinistra del cerchio. I dettagli che non stanno in una riga (gli occhi, la bocca, il cuore della fiamma) si disegnano dopo. Ogni disegno si campiona una volta con getPointAtLength ogni mezza unità; il tratto è vector-effect: non-scaling-stroke, così è largo LINE pixel a 20 come a 28.
La strada è una spezzata di angoli a GAP pixel dal messaggio: sopra la nuvoletta, giù per un fianco, sotto la riga delle reazioni, poi su dentro la pillola. Le misure vengono da offsetLeft e offsetTop sommati fino alla conversazione, non da getBoundingClientRect, così le trasformazioni in corso (la nuvoletta premuta, le pillole che scivolano) non ingannano. Ogni angolo si arrotonda col suo raggio (quello della nuvoletta o della pillola più GAP): l'arco parte a r·tan(φ/2) dall'angolo, col centro sul lato verso cui si gira, e si campiona ogni due pixel; i tratti dritti bastano i due capi. Il verso si sceglie perché il giro tocchi sempre tre lati: dalla metà sinistra della barra in senso orario, dalla destra in senso antiorario passando dietro il tondo dell'iniziale; con la barra sotto il messaggio, o partendo da una pillola, il giro è completo e prende il verso che non si incrocia.
Il disegno nella barra (percorso all'indietro, così la linea esce dal suo piede senza tornanti), il giro e il disegno nella pillola diventano una sola spezzata con le lunghezze cumulate. A ogni fotogramma se ne disegna solo una finestra [coda, testa], trovando i capi con una ricerca binaria: la testa va da un capo all'altro in LAP secondi con power2.inOut, la coda aspetta il 90% di LAP e poi parte con power3.in in ZIP secondi, così la linea si riavvolge come un metro a nastro. La linea è larga come i disegni dove li disegna e TRAIL sul giro, con tre gradini da 6 pixel per parte; la freccetta in testa si orienta sulla tangente presa 2,5 pixel prima e dopo, e compare solo sul giro.
Prima di ogni cambio si legge dove sta ogni pillola, poi le pillole che si spostano partono dal posto vecchio (FLIP). Una pillola che se ne va resta dov'era, fuori dalla fila in posizione assoluta, mentre si chiude; una nuova entra subito nella fila, vuota e invisibile, così la strada sa dove finire, e si apre con back.out a metà del giro. Atterrando, la pillola si gonfia di BOUNCE e torna con elastic.out, il riempimento del disegno passa da fill-opacity 0,85 a 0,22 e il numero rotola: il vecchio esce in alto, il nuovo entra dal basso con back.out, e la larghezza segue con un tween.
Togliendo la propria reazione la finestra corre all'indietro: il capo davanti con power2.out, quello dietro con power2.in, finché si incontrano qualche centinaio di pixel prima (al massimo 230) e la linea svanisce. Così il disegno si srotola dalla sua fine mentre la testa corre via, e la pillola, se era l'ultima reazione, si chiude subito dopo.
La barra è un role toolbar con un tabindex che gira: frecce, Home e Fine si spostano, Invio sceglie, Esc chiude e riporta il fuoco al pulsante React, Tab chiude e prosegue da lì. Ogni pulsante ha aria-label (React with Kudos to Maya’s message) e aria-pressed per la propria reazione, e un annuncio aria-live dice cosa è cambiato. Il puntatore fermo 0,24 secondi sul pulsante apre la barra; il dito tenuto HOLD secondi sull'annuncio la apre e, finché quel dito la tiene aperta, un touchmove non passivo impedisce allo scorrimento di partire, così si può scivolare fino a una reazione e alzare il dito per sceglierla. L'anteprima della libreria apre la barra, prova le reazioni e ne sceglie una senza prendere il fuoco.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
REACTIONS | script.js | [['kudos', 'Kudos'], ['love', 'Love'], ['haha', 'Haha'], ['fire', 'Fire'], ['fast', 'Fast'], ['medal', 'Medal']] | Le reazioni della barra: il disegno (kudos, love, haha, fire, fast o medal) e il nome che si legge e si sente. |
LAP | script.js | 0.62 | I secondi del giro, dalla barra intorno al messaggio fino alla reazione sotto di lui. |
ZIP | script.js | 0.34 | I secondi in cui il resto della linea si riavvolge dietro la testa quando è arrivata. |
GAP | script.js | 7 | A quanti pixel dal messaggio corre la linea. |
LINE | script.js | 2 | La larghezza dei disegni, e della linea dove li disegna, in pixel. |
TRAIL | script.js | 3 | La larghezza della linea durante il giro, in pixel. |
HOLD | script.js | 0.42 | I secondi col dito fermo su un messaggio per aprire la barra. |
BOUNCE | script.js | 0.16 | Quanto si gonfia una reazione quando la linea ci atterra. |
DEMO | script.js | [[2, 'medal', 'bar'], [1, 'kudos', 'bar'], [2, 'fire', 'bar'], [1, 'kudos', 'chip'], [2, 'fire', 'chip']] | L'anteprima della libreria, un passo per clic: il messaggio (da 0), la reazione, e se dalla barra o dalla pillola sotto il messaggio. I cinque passi tornano allo stato di partenza. |
Per le reazioni ai messaggi di una chat di gruppo, di un canale di annunci, dei commenti di una community o di un'app sportiva: ovunque una reazione debba sentirsi consegnata al messaggio, e non solo contata.
La chat prende la larghezza dello schermo meno i margini (358 pixel a 390). Il dito tenuto sull'annuncio apre la barra (la nuvoletta si abbassa un poco mentre aspetta, e il menu del telefono e la selezione del testo restano chiusi); si sceglie toccando, o scivolando col dito fino alla reazione e alzandolo. Le reazioni sono alte 30 pixel ma il bersaglio è di 44, e sotto i 420 pixel il pulsante React, che serve ai tasti e al mouse, lascia il suo spazio alle nuvolette e va in fondo alla riga delle reazioni.
Niente linea e niente spostamenti: la barra compare e scompare sfumando, la reazione scelta cambia colore e numero con una dissolvenza al suo posto, una pillola nuova compare e una tolta svanisce; gli altri annunci si attenuano lo stesso mentre la barra è aperta.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 17 KB; le librerie: GSAP 28 KB.
Provata il 7 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.