Vai al contenuto
animorio
EN
Tutti i componenti

Il pulsante della ceralacca

Pulsanti, SVG, Fisica, TransizioniGSAPImpegnativaPeso compresso: Demo 10 KBGSAP 28 KBGratisScarica lo zip

Il Sign & send di un contratto, chiuso come una lettera: premuto, un bastoncino di ceralacca rosso scuro cola nel pozzetto del pulsante e la cera si allarga viscosa in una pozza dal bordo irregolare. Un sigillo d’ottone scende dall’alto in prospettiva, preme (il pulsante cede e la cera si schiaccia più larga) e si rialza: nella cera resta una & in rilievo, illuminata da filtri di luce SVG veri, con la luce che segue il puntatore. La scritta diventa Sent.

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 wax seal button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/opsz.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/opsz-italic.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the wax seal button: pressed, a stick of sealing wax drips into the round well and stirs a pool with a ragged
         edge, a brass seal comes down from above, presses, lifts, and leaves an ampersand raised in the wax that
         catches the light. The pool and the relief are an SVG path redrawn every frame under a lighting filter -->
    <div class="seal" data-seal>
      <button class="seal__btn" type="button" aria-describedby="seal-live" data-btn data-autoclic>
        <span class="seal__well" aria-hidden="true" data-well>
          <svg class="seal__nib" viewBox="0 0 24 24" data-nib>
            <path d="M12 2.8l5.2 7.6c.3.5.3 1.1 0 1.6L12 21.2 6.8 12c-.3-.5-.3-1.1 0-1.6z"/>
            <path d="M12 21.2v-7"/>
            <circle cx="12" cy="12.4" r="1.6"/>
          </svg>
        </span>

        <!-- the pool of wax, its shadow and the relief: drawn round the middle of the well -->
        <svg class="seal__wax" viewBox="-100 -100 200 200" aria-hidden="true" data-wax>
          <defs>
            <filter id="wax-relief" filterUnits="userSpaceOnUse" x="-100" y="-100" width="200" height="200" color-interpolation-filters="sRGB">
              <!-- the grey of each shape is its height: the pool, the lip pushed up round the die, the pressed field,
                   the ring and the ampersand -->
              <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0.333 0.333 0.334 0 0" result="steps"/>
              <feGaussianBlur in="steps" stdDeviation="0.9" result="fine"/>
              <!-- and the whole pool is a soft dome at its edge -->
              <feGaussianBlur in="SourceAlpha" stdDeviation="3.4" result="dome"/>
              <feComposite in="fine" in2="dome" operator="arithmetic" k2="0.62" k3="0.55" result="height"/>
              <feDiffuseLighting in="height" surfaceScale="5.5" diffuseConstant="1.18" lighting-color="#ffffff" result="diffuse">
                <feDistantLight azimuth="225" elevation="46" data-light/>
              </feDiffuseLighting>
              <feSpecularLighting in="height" surfaceScale="5.5" specularConstant="0.8" specularExponent="26" lighting-color="#ffe7d2" result="gloss">
                <feDistantLight azimuth="225" elevation="46" data-light/>
              </feSpecularLighting>
              <feFlood flood-color="#8e1a22" result="tint" data-tint/>
              <feComposite in="tint" in2="diffuse" operator="arithmetic" k1="1.02" k2="0.16" result="lit"/>
              <feComposite in="lit" in2="SourceAlpha" operator="in" result="body"/>
              <feComposite in="gloss" in2="SourceAlpha" operator="in" result="shine"/>
              <feComposite in="shine" in2="body" operator="arithmetic" k2="0.75" k3="1"/>
            </filter>
            <filter id="wax-shadow" filterUnits="userSpaceOnUse" x="-100" y="-100" width="200" height="200">
              <feGaussianBlur stdDeviation="2.4"/>
            </filter>
          </defs>
          <path class="seal__shadow" transform="translate(1.5 3.5)" data-shadow d=""/>
          <g filter="url(#wax-relief)" data-relief>
            <path data-pool fill="rgb(150,150,150)" d=""/>
            <g data-print opacity="0" transform="scale(1.1)">
              <circle r="27" fill="none" stroke="rgb(196,196,196)" stroke-width="4.6"/>
              <circle r="25" fill="rgb(104,104,104)"/>
              <circle r="21.4" fill="none" stroke="rgb(150,150,150)" stroke-width="1.3"/>
              <text class="seal__mono" y="10.6" text-anchor="middle" fill="rgb(176,176,176)">&amp;</text>
            </g>
          </g>
          <g data-drops></g>
        </svg>

        <span class="seal__words" aria-hidden="true">
          <span class="seal__word is-on" data-word="rest">Sign &amp; send</span>
          <span class="seal__word" data-word="busy">Sealing</span>
          <span class="seal__word" data-word="done">Sent</span>
        </span>
        <span class="sr" data-name>Sign and send the contract</span>
      </button>

      <!-- the stick of sealing wax, and the brass seal seen from above, drawn in perspective every frame -->
      <div class="seal__stick" aria-hidden="true" data-stick>
        <svg viewBox="-12 -150 24 156">
          <defs>
            <linearGradient id="wax-stick" x1="0" x2="1" y1="0" y2="0">
              <stop offset="0" stop-color="#4c070d"/>
              <stop offset="0.3" stop-color="#a1222b"/>
              <stop offset="0.48" stop-color="#d04a4d"/>
              <stop offset="0.62" stop-color="#9a1d26"/>
              <stop offset="1" stop-color="#3d050a"/>
            </linearGradient>
            <linearGradient id="wax-tip" x1="0" x2="0" y1="0" y2="1">
              <stop offset="0" stop-color="#9a1d26"/>
              <stop offset="1" stop-color="#6a0d15"/>
            </linearGradient>
          </defs>
          <rect x="-8" y="-148" width="16" height="136" rx="3" fill="url(#wax-stick)"/>
          <path d="M-8 -14c0 6 3 12 8 14 5-2 8-8 8-14z" fill="url(#wax-tip)"/>
          <path d="M-4.5 -140v120" stroke="#ffd0c4" stroke-opacity="0.35" stroke-width="1.6" stroke-linecap="round"/>
        </svg>
      </div>
      <svg class="seal__stamp" aria-hidden="true" data-stamp>
        <defs>
          <radialGradient id="brass-top" cx="0.36" cy="0.32" r="0.8">
            <stop offset="0" stop-color="#fff3cf"/>
            <stop offset="0.25" stop-color="#e9c779"/>
            <stop offset="0.7" stop-color="#a77a2c"/>
            <stop offset="1" stop-color="#5e3f12"/>
          </radialGradient>
          <linearGradient id="brass-side" x1="0" x2="1" y1="0" y2="1">
            <stop offset="0" stop-color="#d2a656"/>
            <stop offset="0.55" stop-color="#7c561b"/>
            <stop offset="1" stop-color="#3f2a0b"/>
          </linearGradient>
          <radialGradient id="ebony" cx="0.38" cy="0.34" r="0.8">
            <stop offset="0" stop-color="#6c5a50"/>
            <stop offset="0.5" stop-color="#2a1f1b"/>
            <stop offset="1" stop-color="#0d0908"/>
          </radialGradient>
          <filter id="stamp-blur" x="-50%" y="-50%" width="200%" height="200%">
            <feGaussianBlur stdDeviation="4" data-blur/>
          </filter>
        </defs>
        <ellipse class="seal__stamp-shadow" filter="url(#stamp-blur)" data-cast/>
        <g data-parts></g>
      </svg>

      <p class="sr" id="seal-live" 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.

21 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 della ceralacca» da Animorio e adattala al mio sito

Cosa fa

Su un fondo bianco una pillola color avorio, come carta di cotone spessa, con a sinistra un pozzetto tondo incavato con un pennino disegnato a tratto e la scritta Sign & send. Col mouse sopra il pulsante si alza di un pixel e il pennino si inclina come per scrivere. Premendo, il pennino sparisce e la scritta diventa Sealing; da in alto a sinistra entra un bastoncino di ceralacca rosso scuro che si ferma sopra il pozzetto e gira in piccoli cerchi: ne cadono quattro gocce, e ogni goccia atterra con uno schizzo e si fonde con le altre in una pozza che si allarga, oscilla appena e si assesta, col bordo irregolare che si muove piano finché la cera è liquida. Il bastoncino se ne va e scende dall’alto un sigillo d’ottone visto da sopra: il pomello e il collare si stringono avvicinandosi e la sua ombra si fa più netta. Quando tocca, il pulsante si schiaccia e rimbalza, la cera si allarga di colpo in nuovi lobi intorno al sigillo; poi il sigillo si rialza e svanisce e nella cera resta un campo tondo premuto, con un labbro rialzato intorno, un anello sottile e una & in rilievo. La luce passa radente da un lato e sale al suo posto, e i rilievi la prendono; la scritta diventa Sent. Muovendo il mouse la luce viene dalla sua parte e le ombre del rilievo girano. Un altro clic stacca il sigillo, che si solleva e svanisce, e torna Sign & send.

Come funziona

La pozza è un campo di metaball

Ogni goccia è una sfera di un campo: in un punto il campo vale la somma di r² / d² di tutte le gocce, e il bordo della pozza è dove vale 1. Lontano dalle gocce le aree si sommano: quattro gocce di raggio r fanno una pozza di raggio 2r, quindi POOL / √DROPS è il raggio di ognuna. A ogni fotogramma il bordo si ritrova lungo 64 raggi dal centro: si scende da fuori a passi di 3 pixel finché il campo arriva a 1, poi nove bisezioni. I 64 punti diventano una curva chiusa di Catmull-Rom scritta come Bézier cubiche nel d del tracciato.

Viscosa: molle e armoniche

Ogni goccia atterra piccola e veloce e il suo raggio va verso quello giusto su una molla smorzata (rigidezza 150, smorzamento THICK): la cera si spande, supera un poco la misura e torna, densa. Il bordo è irregolare per sei armoniche (3, 4, 5, 7, 9 e 12 lobi) di ampiezze e fasi a caso, pesate da RAGGED; finché la cera è liquida le fasi girano piano e il bordo si muove, quando il sigillo preme metà armoniche cambiano (nuovi lobi schiacciati) e la liquidità scende a zero in 1,6 secondi: la forma si fissa.

Il rilievo con la luce vera

Dentro il gruppo filtrato il grigio di ogni forma è un’altezza: la pozza, il labbro spinto in su intorno al sigillo, il campo premuto più basso, l’anello e la &. Un feColorMatrix porta la luminosità nell’alfa, un feGaussianBlur leggero la smussa e un secondo blur ampio dell’alfa fa la cupola del bordo; le due cose sommate con un feComposite aritmetico sono la mappa d’altezza. feDiffuseLighting e feSpecularLighting con una luce lontana la illuminano, il colore della cera è moltiplicato per la diffusa (più un po’ di luce d’ambiente) e il lucido è sommato sopra, tutto ritagliato sulla forma.

Il sigillo in prospettiva

Il sigillo visto dall’alto è una pila di dischi alle loro altezze: il conio d’ottone, il collare, il collo d’ebano e il pomello. Ogni disco è proiettato da una camera appesa poco sopra e a destra del pozzetto, k = 900 / (900 − z): più è in alto più è grande e spostato verso l’esterno, così il cilindro mostra il fianco e si legge in 3D. Mentre scende i dischi si stringono e l’ombra sfocata sul pulsante si avvicina, si fa più netta e più scura.

La sequenza

Una timeline GSAP fa entrare il bastoncino, lo fa girare in cerchi mentre lascia cadere le gocce (ogni goccia è un’ellisse che cade con un’accelerazione e, toccando, diventa una sfera del campo), lo porta via, fa scendere il sigillo e al contatto schiaccia il pulsante (scaleY con un ritorno elastico) e allarga le sfere di SPREAD. Al rialzo compare l’impronta e la luce passa radente da −80 gradi e 24 di elevazione al suo posto. Il ciclo di disegno dorme quando niente si muove.

Valori da cambiare

NomeFileDi partenzaCosa cambia
DROPSscript.js4Quante gocce di cera cadono dal bastoncino.
POOLscript.js35Il raggio della pozza prima che il sigillo prema, in pixel.
SPREADscript.js1.17Di quanto il sigillo schiaccia la pozza più larga.
RAGGEDscript.js0.075Quanto è irregolare il bordo della pozza (0 = un cerchio perfetto).
THICKscript.js0.42Come si assesta la cera: basso, trema come gelatina; vicino a 1, scivola come miele.
PRESSscript.js0.07Quanto cede il pulsante sotto il sigillo.
LIGHTscript.js1Quanto la luce sul rilievo segue il puntatore (0 = resta in alto a sinistra).
MARKscript.js&Che cosa lascia in rilievo il sigillo nella cera: un carattere o due iniziali.

Provali nel Playground

Quando usarla

Per l’azione che chiude qualcosa di importante: firmare e mandare un contratto, confermare un invito di nozze, spedire una lettera, accettare un’offerta. Sta bene in un prodotto di firma elettronica, nel sito di un matrimonio o di uno studio legale, in una newsletter scritta a mano. Il sigillo chiede qualche secondo: non va su un’azione che si ripete spesso.

Sul telefono

Il pulsante resta della stessa misura (circa 290 pixel a 390) con meno spazio ai lati; il bastoncino e il sigillo entrano da sopra e da sinistra, mai sopra la scritta, e niente sfora. Il tocco fa partire la sequenza; la luce del rilievo, senza un puntatore, resta in alto a sinistra.

Con il movimento ridotto

Niente bastoncino, niente gocce, niente sigillo che scende né pulsante che si schiaccia: il pennino svanisce e il sigillo già impresso compare in dissolvenza nel pozzetto, con la scritta che cambia senza scorrere. La luce non segue il puntatore. Un altro clic lo fa svanire.

Trappole

  • Un fromTo di GSAP dentro una timeline si applica subito, anche se è in coda: la luce partiva radente e bassa dal primo istante e la pozza appariva scura per tutta la colata. Coi tween che arrivano dopo ci vuole immediateRender: false.
  • La funzione che seguiva i tween in corso sostituiva il loro onComplete: le gocce cadevano ma non diventavano mai cera. Il nuovo onComplete deve chiamare anche quello di prima.
  • Le altezze non si possono fare con l’opacità: due forme sovrapposte sommano l’alfa e un campo premuto più basso della pozza diventa impossibile. Il grigio di forme opache, dipinte una sopra l’altra, si comporta come una mappa d’altezza vera, e un feColorMatrix lo porta nell’alfa.
  • Con la camera al centro del pulsante il sigillo, ancora in alto, finiva molto a sinistra del pozzetto e sembrava arrivare da fuori: la camera sta appena sopra e a destra del pozzetto.
  • Con l’opacità che cresceva per tutta la discesa il sigillo restava trasparente fino al contatto e la cera si vedeva attraverso: l’opacità arriva piena nei primi 0,2 secondi.
  • Il pennino e la scritta stanno a sinistra e a destra del pozzetto: il bastoncino entra da in alto a sinistra perché, inclinato verso destra, passava sopra le parole.

Librerie

GSAP 3.15.0, Fraunces (font variabile) 5.3.0.

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

Collaudo

Provata il 5 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