Vai al contenuto
animorio
EN
Tutti i componenti

Il pulsante del buono sconto

Pulsanti, Fisica, SVGGSAP, Solo CSSImpegnativaPeso compresso: Demo 14 KBGSAP 28 KBGratisScarica lo zip

La riga Apply coupon di un carrello, con un buono di carta arancione nella pillola: il codice AUTUMN15, un talloncino con una foglia e una fila di fori fra i due. Al clic il buono si strappa lungo i fori: il bordo frastagliato si calcola di nuovo ogni volta, si vede l'anima bianca della carta, e la metà strappata gira intorno al punto dove tiene ancora, si stacca e cade ruotando come una carta, poi svanisce sotto la pillola. Il buono si può anche tirare con la mano. Dopo Checking… una penna barra €120.00, il prezzo vecchio si rimpicciolisce in alto e quello nuovo scende con le cifre che girano sulle loro ruote fino a €102.00, e arriva un distintivo −15%. Un altro clic toglie lo sconto: il tratto si cancella, le cifre risalgono e un buono nuovo sale al suo posto.

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 coupon button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/jetbrains-mono@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the coupon button: the Apply coupon line of a cart, with a small paper coupon in the pill, perforated down its
         stub. A click (or a pull on the coupon) tears it along the holes: the torn edge is worked out anew every time,
         the torn half falls away spinning, and the price is struck through with a pen and rolls down to the new one.
         Another click takes the discount off and a fresh coupon rises in its place -->
    <div class="deal" data-deal>
      <button class="deal__btn" type="button" data-btn data-autoclic>
        <span class="deal__fill" aria-hidden="true"></span>
        <svg class="deal__coupon" viewBox="0 0 104 42" aria-hidden="true" data-coupon>
          <defs>
            <linearGradient id="cp-paper" x1="0" y1="0" x2="0.35" y2="1">
              <stop offset="0" class="cp-a"/>
              <stop offset="1" class="cp-b"/>
            </linearGradient>
            <pattern id="cp-print" width="3.2" height="3.2" patternUnits="userSpaceOnUse" patternTransform="rotate(32)">
              <path class="cp-print" d="M0 1.6h3.2"/>
            </pattern>
            <clipPath id="cp-cut-stub"><path data-cut="stub"/></clipPath>
            <clipPath id="cp-cut-voucher"><path data-cut="voucher"/></clipPath>
            <g id="cp-art">
              <!-- the paper: a ticket with a notch top and bottom where the perforation runs -->
              <path class="cp-paper" d="M3.5 0H18.6A3.4 3.4 0 0 0 25.4 0H100.5A3.5 3.5 0 0 1 104 3.5V38.5A3.5 3.5 0 0 1 100.5 42H25.4A3.4 3.4 0 0 0 18.6 42H3.5A3.5 3.5 0 0 1 0 38.5V3.5A3.5 3.5 0 0 1 3.5 0Z"/>
              <path class="cp-printed" d="M3.5 0H18.6A3.4 3.4 0 0 0 25.4 0H100.5A3.5 3.5 0 0 1 104 3.5V38.5A3.5 3.5 0 0 1 100.5 42H25.4A3.4 3.4 0 0 0 18.6 42H3.5A3.5 3.5 0 0 1 0 38.5V3.5A3.5 3.5 0 0 1 3.5 0Z"/>
              <rect class="cp-dash" x="28.5" y="4.5" width="71" height="33" rx="2.4"/>
              <!-- a fallen leaf on the stub -->
              <g class="cp-leaf" transform="translate(11 21) rotate(-28)">
                <path d="M0 -8.2C3.6 -5.6 4.6 -1.6 3.4 2.2C2.6 4.6 1.2 6.2 0 7C-1.2 6.2 -2.6 4.6 -3.4 2.2C-4.6 -1.6 -3.6 -5.6 0 -8.2Z"/>
                <path class="cp-vein" d="M0 -6.6V9.6M0 -2.6L2.4 -4.4M0 -2.6L-2.4 -4.4M0 0.9L2.8 -0.9M0 0.9L-2.8 -0.9M0 4.1L2.2 2.6M0 4.1L-2.2 2.6"/>
              </g>
              <text class="cp-off" x="34" y="16.2" data-off-text>15% OFF</text>
              <text class="cp-code" x="33.6" y="30.4" data-code-text>AUTUMN15</text>
            </g>
          </defs>
          <g class="cp-piece" data-piece="stub">
            <g clip-path="url(#cp-cut-stub)"><use href="#cp-art"/></g>
            <path class="cp-fiber" data-fiber="stub"/>
          </g>
          <g class="cp-piece" data-piece="voucher">
            <g clip-path="url(#cp-cut-voucher)"><use href="#cp-art"/></g>
            <path class="cp-fiber" data-fiber="voucher"/>
          </g>
        </svg>
        <span class="deal__faces" data-faces>
          <span class="deal__face" data-face="rest">
            <span class="deal__word" data-wide="Apply coupon" data-rest-text>Apply coupon</span>
          </span>
          <span class="deal__face deal__face--busy" data-face="busy" aria-hidden="true">
            <span class="deal__shine" data-busy-text>Checking…</span>
          </span>
          <span class="deal__face deal__face--done" data-face="done" aria-hidden="true">
            <span class="deal__lines">
              <span class="deal__applied" data-applied-text>Coupon applied</span>
              <span class="deal__sub"><span class="deal__code" data-code-small>AUTUMN15</span> · <span data-remove-text>Remove</span></span>
            </span>
          </span>
        </span>
        <span class="deal__chip" aria-hidden="true" data-chip>−15%</span>
        <span class="deal__price" data-price>
          <span class="deal__was" aria-hidden="true" data-was>
            <span data-was-text>€120.00</span>
            <svg class="deal__strike" aria-hidden="true" data-strike-svg><path data-strike/></svg>
          </span>
          <span class="deal__now" data-now></span>
        </span>
      </button>
      <p class="sr" aria-live="polite" data-live></p>
    </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.

26 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 del buono sconto» da Animorio e adattala al mio sito

Cosa fa

Su un fondo nero una pillola color carbone caldo, alta 60 pixel, con un filo di luce in alto. A sinistra, appoggiato nella pillola con la sua ombra, un buono di carta arancione: il talloncino con una foglia avorio e le sue nervature, una fila di forellini con una tacca tonda in alto e in basso, e il buono con la cornice tratteggiata, 15% OFF in piccolo e AUTUMN15 in caratteri da macchina, su una stampa fine di righe oblique. Poi, in Mona Sans avorio, Apply coupon, e a destra il totale €120.00. All'arrivo il buono sale da sotto con un rimbalzo. Passando sopra la pillola la scritta si allarga, il fondo si gonfia con un rimbalzo e il buono dà un piccolo strattone sulla perforazione, come provato da un dito. Al clic la scritta diventa Checking…, con un riflesso che la attraversa, e lo strappo scende lungo i fori: il buono gira intorno al punto dove tiene ancora, e lungo i due bordi frastagliati, diversi a ogni volta, si vede il bianco della carta. Arrivato in fondo il buono si stacca e cade ruotando e scivolando di lato come una carta, poi svanisce sotto la pillola; il talloncino trema. La pillola si stringe sul talloncino e la scritta lo segue. Poi una penna arancione barra il prezzo con un tratto veloce e un uncino; il prezzo barrato si rimpicciolisce in alto, spento, e sotto il prezzo nuovo scende con le cifre che girano sulle loro ruote fino a €102.00. Arriva con un rimbalzo un distintivo arancione −15%, e la scritta dice Coupon applied con sotto AUTUMN15 · Remove. Un altro clic toglie lo sconto: il distintivo sparisce, il tratto di penna si cancella, il prezzo vecchio torna giù e le cifre risalgono a €120.00, il talloncino scivola fuori e un buono nuovo, con uno strappo nuovo che aspetta nei fori, sale al suo posto. Il buono si può anche tirare a destra o in giù: lo strappo segue la mano, lasciato oltre metà si strappa del tutto e cade con la spinta della mano, prima torna al suo posto. Con la tastiera il fuoco mostra un anello avorio e il buono dà lo strattone; Invio o Spazio applicano.

Come funziona

Un buono in SVG tagliato in due

Il buono è un gruppo SVG in defs (la carta col suo gradiente, una stampa fine di righe oblique, la cornice tratteggiata, la foglia sul talloncino, 15% OFF e il codice in JetBrains Mono) usato due volte con use: una copia è il talloncino, l'altra il buono, e ognuna ha il suo clipPath. I due clipPath si toccano lungo la linea di strappo, quindi a riposo si vede un buono intero; i fori della perforazione non sono disegnati, sono i buchi che restano fuori da tutti e due i ritagli, e si vede la pillola attraverso.

Lo strappo calcolato

A ogni buono nuovo un generatore casuale con un seme nuovo costruisce lo strappo: fra un foro e l'altro (ogni 3,1 unità, raggio 0,72) il ponticello di carta cede lungo tre o quattro punti spostati a caso di lato, di più a metà del ponticello (seno), quanto dice RAGGED; a ogni foro il talloncino tiene il suo mezzo cerchio di sinistra e il buono quello di destra. Così i due bordi combaciano, e il bordo non è mai uguale due volte. Lungo la parte già strappata si disegna un tratto avorio, l'anima bianca della carta sotto la stampa.

Lo strappo che scende e la caduta

Mentre lo strappo scende (tear.t da 0 a 1 in TEAR secondi, oppure seguendo la mano) il buono gira intorno al punto dove tiene ancora, fino a 11 gradi. Arrivato in fondo diventa una carta libera: il centro e l'angolo si ricavano dalla rotazione intorno all'ultimo punto, la velocità da una piccola spinta (più quella della mano, se è stato tirato), e da lì gravità (GRAVITY), resistenza dell'aria più forte di traverso che in giù, una spinta di lato che segue sin 2a come una carta lasciata cadere piatta, e una rotazione che rallenta (SPIN). Passi di 1/240 di secondo, fotogramma limitato a 1/30; dopo venti pixel di caduta il buono svanisce in ottanta. Il talloncino riceve un colpo su una molla e trema.

La penna e le ruote delle cifre

Il tratto di penna è un path nuovo a ogni sconto (un poco in salita, curvo, con l'uncino dove la penna si alza), disegnato con stroke-dashoffset. Il prezzo è fatto di ruote: ogni cifra che cambia è una striscia con uno spazio vuoto e tre giri di cifre dietro una finestra alta una cifra, con una maschera che la sfuma in alto e in basso; il prezzo pieno usa il terzo giro e quello scontato il secondo, quindi scendendo ogni ruota gira in giù e tornando gira in su. Il prezzo vecchio è composto nelle stesse celle larghe uguali, così sta esattamente sopra le ruote quando la penna lo barra, e poi si rimpicciolisce in alto.

Tirare il buono

Il buono si può anche tirare: pointerdown sul buono (non sul talloncino) cattura il puntatore, e lo spostamento verso destra o in basso diventa lo strappo, (dx · 0,7 + dy · 0,7) / 70. Lasciato oltre metà finisce di strapparsi e la velocità della mano passa alla caduta; prima torna al suo posto con un elastico. Dopo un trascinamento il clic che segue viene ignorato. Il buono ha touch-action: none, così sul telefono il dito tira invece di far scorrere la pagina; un tocco semplice resta un clic.

Gli stati

Il pulsante è un button vero con un aria-label che dice tutto (Apply coupon AUTUMN15. Total €120.00; da applicato il codice, lo sconto, il totale nuovo e quello vecchio, e Remove coupon), una zona aria-live (Applying AUTUMN15…, AUTUMN15 applied: 15% off. New total €102.00., Coupon removed.) e aria-disabled mentre lavora. Il controllo del codice è simulato. Applicare dura circa tre secondi, più dei 2,8 fra un clic e l'altro dell'anteprima: il secondo clic cade nell'attesa e lo sconto resta in vista un giro intero.

Valori da cambiare

NomeFileDi partenzaCosa cambia
LABELscript.jsApply couponLa scritta a riposo. Se è troppo lunga per lo spazio, il carattere si rimpicciolisce.
CODEscript.jsAUTUMN15Il codice stampato sul buono, ripetuto sotto Coupon applied e nella zona aria-live.
PERCENTscript.js15Lo sconto in percento: cambia il buono (15% OFF), il distintivo e il prezzo nuovo.
PRICEscript.js120Il totale del carrello prima dello sconto; quello scontato si calcola.
CURRENCYscript.js€Il simbolo scritto prima del prezzo.
BUSYscript.jsChecking…La scritta mentre il codice si controlla (simulato).
APPLIEDscript.jsCoupon appliedLa scritta a sconto fatto.
REMOVEscript.jsRemoveLa parolina che dice che un altro clic toglie lo sconto.
TEARscript.js0.34I secondi che lo strappo ci mette a scendere lungo la perforazione.
RAGGEDscript.js1Quanto è frastagliato il bordo strappato: 0 pulito da foro a foro, 2 molto ruvido.
GRAVITYscript.js1500Quanto forte cade la metà strappata, in pixel al secondo al quadrato.
SPINscript.js1Quanto gira la metà strappata cadendo: 0 cade diritta.
ROLLscript.js0.9I secondi in cui le cifre girano fino al prezzo nuovo.

Provali nel Playground

Quando usarla

Per il codice sconto di un carrello o di una cassa: il buono di una stagione, il codice di benvenuto, quello di un influencer, una carta regalo. Sta bene nei negozi di moda, di casa, di cosmetica e in ogni promozione stagionale. Il controllo del codice è simulato: in un negozio vero il clic manda il codice al server, e lo strappo può partire subito mentre la penna aspetta la risposta.

Sul telefono

La pillola a riposo è larga circa 345 pixel e quella scontata circa 320: a 390 pixel ci stanno, e se la scritta è più lunga dello spazio il carattere si rimpicciolisce. Il tocco strappa il buono; il dito può anche tirarlo, e sopra il buono la pagina non scorre. Il buono che cade esce sotto la pillola e svanisce prima di arrivare in fondo allo schermo.

Con il movimento ridotto

Niente strappo che scende, niente caduta e niente ruote che girano: al clic il buono svanisce lasciando il talloncino col bordo strappato, la pillola prende la nuova larghezza mentre le parole sono sparite, e prezzo barrato, prezzo nuovo e distintivo arrivano in dissolvenza. Il buono non dà lo strattone e la pillola non si gonfia (sotto il mouse il fondo si schiarisce appena). Un altro clic fa sparire tutto in dissolvenza e un buono nuovo appare in dissolvenza.

Trappole

  • Il prezzo spariva per un secondo e mezzo appena dopo il clic: il fromTo che più tardi fa scendere il prezzo nuovo da opacità 0 applicava subito il suo stato di partenza, al momento in cui la timeline veniva costruita. Ha immediateRender: false, come quello del distintivo.
  • Il prezzo barrato spariva mentre si rimpiccioliva: GSAP non sa interpolare color da var(--ink) a var(--muted) e a metà scriveva un colore non valido. I due colori si leggono da :root e si interpolano come valori veri.
  • Con font-variant-numeric: tabular-nums Mona Sans usa uno zero tagliato, e €120.00 sembrava un codice. Niente cifre tabulari: ogni cifra sta in una cella larga 0,64 em, sia nelle ruote sia nel prezzo vecchio, così le due scritte coincidono.
  • La metà strappata partiva verso destra a 70 pixel al secondo e passava sopra Checking…; e la pillola si stringeva mentre era ancora lì, portandole incontro la scritta. Ora la spinta di lato è 22 e la pillola si stringe solo quando il buono è sceso sotto.
  • Il buono nuovo saliva mentre Apply coupon stava ancora scivolando a destra, e per un attimo la scritta gli passava sotto. La scritta torna subito, il buono nuovo sale dopo.

Librerie

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0, JetBrains Mono (font variabile) 5.3.0.

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

Collaudo

Provata il 10 ottobre 2026

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

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