Vai al contenuto
animorio
EN
Tutti i componenti

Il pulsante d’invio

Pulsanti, Transizioni, SVGGSAPMediaPeso compresso: Demo 7,2 KBGSAP 28 KBGratisScarica lo zip

Il pulsante di un’iscrizione che passa per tutti gli stati di un invio: alla pressione la scritta se ne va e l’aeroplanino di carta decolla, la pillola si stringe in un cerchio con un anello che gira, poi una spunta verde che si disegna da sola, oppure una croce rossa e una scossa di testa, e di nuovo una pillola.

Il codice

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 send button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/dm-sans@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <div class="signup-box" data-box>
      <!-- the form: the button goes through its states (sending, sent, failed) and says each one out loud too -->
      <form class="signup" data-form novalidate>
        <label class="field">
          <span class="sr">Your email address</span>
          <input class="field__input" type="email" name="email" value="ada@example.com" autocomplete="email" placeholder="you@example.com" aria-describedby="stato" data-email required>
        </label>
        <div class="send-slot" data-slot>
          <button class="send" type="submit" data-send data-autoclic>
            <span class="send__halo" data-halo aria-hidden="true"></span>
            <span class="send__label" data-label>Subscribe</span>
            <svg class="send__plane" data-plane viewBox="0 0 24 24" aria-hidden="true"><path d="M21 3 10.5 13.5M21 3l-6.5 18-4-7.5L3 10z"/></svg>
            <svg class="send__state" data-state viewBox="0 0 48 48" aria-hidden="true">
              <circle class="send__track" data-track cx="24" cy="24" r="17"/>
              <circle class="send__arc" data-arc cx="24" cy="24" r="17"/>
              <path class="send__tick" data-tick d="M15.5 24.5l6 6 11-12.5"/>
              <path class="send__cross" data-cross d="M17.5 17.5l13 13M30.5 17.5l-13 13"/>
            </svg>
          </button>
        </div>
      </form>
      <p class="status" id="stato" role="status" aria-live="polite" data-status>No spam. Leave with one click.</p>

      <!-- for the demo: the next send fails, to see the other answer -->
      <label class="fail">
        <input type="checkbox" data-fail>
        <span class="fail__box" aria-hidden="true"><svg viewBox="0 0 16 16"><path d="M3.5 8.5l3 3 6-7"/></svg></span>
        <span>Make the next send fail</span>
      </label>
    </div>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

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.

18 valori da cambiare

Oppure chiedila a Claude Code

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 pulsante d’invio» da Animorio e adattala al mio sito

Cosa fa

Un modulo d’iscrizione su un fondo bianco: un campo con l’indirizzo e il pulsante Subscribe con un aeroplanino di carta. Premendo, il pulsante si schiaccia e torna su una molla, la scritta sale e sparisce, e l’aeroplanino si accuccia, gira il muso verso destra, prende la rincorsa e si alza sempre di più, finché esce dal pulsante e svanisce. Intanto la pillola si stringe in un cerchio e dentro compare un anello che gira, con l’arco che si allunga e si accorcia. L’invio, finto, dura 1,4 secondi; quando arriva la risposta l’anello si chiude. Con un sì il cerchio diventa verde, una spunta si disegna da sola, il pulsante si gonfia un attimo e ne esce un anello verde; con un no diventa rosso, una croce si disegna un tratto alla volta e il pulsante scuote la testa. Dopo 2,4 secondi torna una pillola, con «Subscribe» o con «Try again», e un aeroplanino nuovo atterra al suo posto. Ogni stato si legge anche nella riga sotto il campo. Un indirizzo senza @ o senza punto non parte: il campo trema, diventa rosso e dice cosa manca. La casella «Make the next send fail» fa fallire l’invio dopo, poi si toglie da sola. Sotto il mouse l’aeroplanino si sporge in avanti.

Come funziona

Il posto fisso

Il pulsante sta in uno spazio largo quanto la sua pillola più larga: quando i caratteri sono caricati lo script lo misura con tutte le parole che il pulsante dirà (Subscribe, Try again, Sending, Sent, Not sent) e scrive la misura in una variabile CSS. Così, mentre la pillola si stringe in un cerchio e si riapre con un’altra parola, il campo accanto non si sposta mai. Sul telefono il pulsante è largo quanto il modulo e non c’è niente da misurare.

Il decollo

La scritta sale di 18 pixel e sparisce in 0,26 secondi. L’aeroplanino si accuccia e gira il muso di 32 gradi in 0,14 secondi, poi vola per FLIGHT secondi con tre tween insieme: 112 pixel a destra con power2.in, 84 in su con power4.in, e la rotazione a −14 gradi mentre rimpicciolisce. Sono le due curve diverse a fare la traiettoria: la salita parte dopo e cresce più in fretta, quindi prima c’è la rincorsa e poi il volo.

Da pillola a cerchio

La larghezza del pulsante va alla sua altezza in SHRINK secondi con power3.inOut, e con gli angoli tondi a 999px la pillola diventa un cerchio. Dentro c’è un SVG con due cerchi, la traccia e l’arco: l’arco è un tratto di tratteggio, gira con un tween infinito e intanto il suo stroke-dashoffset va avanti e indietro, così si allunga e si accorcia.

La risposta

L’invio finto, fakeSend, è una Promise che si risolve dopo LOADING secondi; in un modulo vero diventa la fetch al servizio della newsletter. Il segno aspetta sia la risposta sia la fine del cerchio, con Promise.all. L’arco si chiude portando lo stroke-dashoffset a zero, il colore cambia e la spunta, o la croce, si disegna: è un tratteggio lungo quanto la linea (getTotalLength), spinto fuori vista e riportato a zero. Un sì gonfia il pulsante a POP e fa partire un anello verde; un no lo scuote con SHAKE, oscillazioni ognuna più piccola della precedente.

Il ritorno

Dopo HOLD secondi il segno sparisce e la pillola si riapre. Lo script mette la parola nuova, legge la larghezza naturale del pulsante senza la larghezza scritta, la rimette e anima fino a lì; alla fine la toglie (clearProps), così il pulsante torna a seguire il CSS. La parola sale al suo posto e un aeroplanino nuovo arriva dal basso a sinistra.

Le parole

Ogni stato si dice anche a parole, in un paragrafo con role="status" che gli screen reader leggono da soli. Durante l’invio il pulsante ha aria-disabled e un’etichetta con lo stato, il campo è in sola lettura e un secondo clic non fa partire niente. Un indirizzo sbagliato prende aria-invalid, il messaggio spiega cosa manca e torna normale appena si scrive.

Valori da cambiare

NomeFileDi partenzaCosa cambia
LOADINGscript.js1.4I secondi che dura l’invio finto (uno vero dura quello che dura).
SHRINKscript.js0.5I secondi della pillola che diventa un cerchio, e del ritorno.
HOLDscript.js2.4I secondi in cui la risposta resta sul pulsante prima che torni una pillola.
SHAKEscript.js[10, 3]La scossa di un no: quanto va lontano, in pixel, e quante volte.
FLIGHTscript.js0.8I secondi del volo dell’aeroplanino.
POPscript.js1.12Quanto si gonfia per un attimo il pulsante su un sì.

Provali nel Playground

Quando usarla

Per un modulo con un’azione sola che chiede un’attesa: iscriversi, mandare un messaggio, salvare, prenotare. Funziona bene quando la risposta arriva in uno o due secondi; per attese più lunghe serve una barra che dica a che punto si è.

Sul telefono

Il campo e il pulsante vanno uno sotto l’altro e il pulsante è largo quanto il modulo: la pillola larga si stringe in un cerchio al centro e poi si riapre fino ai bordi. Il resto è uguale, e il tocco fa da pressione.

Con il movimento ridotto

Il pulsante non cambia forma e niente vola né trema: la scritta cambia in dissolvenza (Sending, Sent o Not sent), il fondo diventa verde o rosso e poi torna scuro, e la riga sotto il campo dice ogni stato come sempre.

Trappole

  • Il segno va disegnato quando la risposta è arrivata e il cerchio è finito, non prima: con una risposta molto veloce, senza Promise.all la spunta comparirebbe dentro un pulsante ancora a metà fra pillola e cerchio.
  • L’invio parte dal clic del pulsante, non dall’evento submit del modulo: dentro un iframe con la sandbox senza allow-forms (un playground, un editor online) il modulo non viene mai inviato e il submit non arriva, mentre il clic sì, anche quello che il browser fa partire quando si preme Invio nel campo.
  • La scritta resta «Subscribe» anche quando è invisibile, e gli screen reader la leggerebbero ancora: per questo durante l’invio il pulsante ha un aria-label con lo stato vero.
  • Un pulsante disattivato con disabled perde il focus, e chi usa la tastiera si ritrova chissà dove nella pagina: qui si usa aria-disabled e si ignora un nuovo invio finché il primo non è finito.
  • La larghezza animata va tolta alla fine con clearProps: altrimenti il pulsante resta largo un numero fisso di pixel e non segue più il CSS quando la finestra cambia.

Librerie

GSAP 3.15.0, DM Sans (font variabile) 5.3.0.

Compressi con gzip, i tre file della demo pesano 7,2 KB; le librerie: GSAP 28 KB.

Collaudo

Provata il 2 ottobre 2026

  • Computer: Fatto
  • Telefono: Fatto
  • Movimento ridotto: Fatto
  • Safari su iPhone: Fatto

Licenza d’uso

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.

Tutti i componenti