Vai al contenuto
animorio
EN
Tutti i componenti

Il compositoio dei caratteri

Caricamento, Testo, Transizioni, FisicaGSAP, Canvas, Solo CSSImpegnativaPeso compresso: Demo 15 KBGSAP 28 KBGratisScarica lo zip

La generazione di una fattura in PDF fatta come la composizione a caratteri mobili: premuto Generate invoice, i caratteri di piombo, con la lettera in rilievo e a rovescio, cadono uno dopo l’altro nel compositoio d’acciaio, si serrano con un piccolo colpo e ogni riga piena scende nel vantaggio d’ottone. Poi il rullo inchiostra la forma, il torchio si abbassa con un tonfo e il foglio si solleva e si gira: la fattura stampata dritta, con l’inchiostro vero. Una volta su due cade un carattere sbagliato, che salta fuori, e Retry rimette quello giusto.

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, viewport-fit=cover">
  <title>The composing stick loader</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the composing stick loader: an invoice being generated, shown as a page of metal type being set by hand. Pressed,
         the sorts drop one after the other into the steel composing stick, their letters raised and back to front; a
         full line is tightened with a tap and slides down into the brass galley. When the forme is complete a roller
         inks it, the platen comes down with the sheet and lifts, and the sheet turns over with the invoice printed the
         right way round. The metal is drawn in HTML and CSS by script.js, the print is painted on a canvas -->
    <section class="press" aria-label="Invoice generation" data-press>
      <div class="press__scene" role="progressbar" aria-label="Invoice generation" aria-valuemin="0" aria-valuemax="100"
           aria-valuenow="0" aria-valuetext="Ready to generate" data-scene>
        <div class="press__fit" data-fit>
          <div class="press__box" aria-hidden="true" data-box>
            <div class="galley">
              <div class="galley__floor"></div>
              <div class="galley__rim galley__rim--top"></div>
              <div class="galley__rim galley__rim--side"></div>
              <div class="galley__rim galley__rim--foot"></div>
            </div>
            <div class="stick">
              <div class="stick__back"></div>
              <div class="stick__bed"></div>
              <div class="stick__clamp"></div>
              <div class="stick__knee"></div>
            </div>
            <div class="press__type" data-type></div>
            <div class="stick__lip"></div>
            <div class="roller" data-roller><div class="roller__drum" data-drum></div></div>
            <div class="sheet__shadow" data-sheet-shadow></div>
            <div class="sheet" data-sheet>
              <canvas class="sheet__face sheet__face--back" data-back></canvas>
              <canvas class="sheet__face sheet__face--front" data-front></canvas>
            </div>
            <div class="platen" data-platen><div class="platen__ribs"></div></div>
            <div class="press__fly" data-fly></div>
          </div>
        </div>
      </div>
      <p class="press__status" aria-hidden="true">
        <svg class="press__tick" viewBox="0 0 24 24" data-tick><path d="M5.5 12.6l4.2 4.2 8.8-9.4" pathLength="1"/></svg>
        <svg class="press__warn" viewBox="0 0 24 24" data-warn><path d="M12 7.5v6M12 16.6v.1" pathLength="1"/><path d="M10.3 3.9L2.6 17.6a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg>
        <span class="press__swap" data-status>Ready to generate</span>
        <span class="press__pct" data-pct></span>
      </p>
      <div class="press__buttons">
        <button class="pill is-quiet" type="button" hidden data-again><span class="pill__label">Generate again</span></button>
        <button class="pill" type="button" data-go data-autoclic><span class="pill__label" data-label>Generate invoice</span></button>
      </div>
      <p class="sr" role="status" aria-live="polite" data-live></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>

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.

47 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 compositoio dei caratteri» da Animorio e adattala al mio sito

Cosa fa

Su un fondo nero, da solo al centro, un compositoio d’acciaio visto dall’alto e un poco di fronte: la sponda dietro, la squadra fissa a destra, il cursore che fissa la giustezza a sinistra e il bordo davanti lucido. Sotto c’è un vantaggio d’ottone con le sue sponde, poi la riga Ready to generate e la pillola Generate invoice. Puntando la pillola, sopra la squadra compare il primo carattere che aspetta, un poco inclinato. Premuta, i caratteri cadono uno alla volta nel compositoio partendo dalla squadra, ruotando mentre scendono: blocchetti di piombo con l’occhio che prende la luce, il fusto con la tacca e sull’occhio la lettera in rilievo, a rovescio, in un carattere graziato variabile. Ogni carattere arriva un filo largo e la riga sobbalza; finita la riga, i quadrati (spazi di metallo più bassi del carattere) entrano da sinistra a riempire la giustezza, un colpetto serra la riga con una molla e un riflesso corre sul metallo, e la riga si alza dal compositoio e cade nel vantaggio sulla sua interlinea, con un piccolo rimbalzo. La riga dice Laying out INV-0412 e la percentuale. Composte le quattro righe (INVOICE INV-0412, Marlow & Finch, Due 30 October, Total €1,840.00, ognuna col suo peso), passa il rullo e le lettere diventano nere d’inchiostro riga per riga (Rendering PDF). Poi la platina di ghisa scende col foglio, batte con un tonfo che scuote tutto e si rialza: si vede il dorso del foglio, con le lettere a rovescio appena in rilievo; il foglio si solleva e si gira, e davanti c’è la fattura stampata dritta, con la grana della carta, il bordo d’inchiostro più scuro, i puntini dove l’inchiostro è mancato e il solco dell’impressione. La riga diventa Invoice ready con una spunta verde che si disegna, la pillola Download PDF (poi Downloaded) e accanto Generate again, che porta via il foglio, scompone la forma e ricomincia. Una generazione su due, nella terza riga cade una d al posto della b (a rovescio si confondono): il carattere si colora di rosso, la riga trema, lui salta fuori dal compositoio girando e cade via; la riga dice Glyph mismatch on line 3 con un triangolo d’avviso e la pillola Retry rimette il carattere giusto e continua da lì.

Come funziona

La larghezza di ogni carattere

Ogni carattere è largo esattamente quanto l’avanzamento della sua lettera, misurato con measureText su un canvas nel carattere variabile Fraunces, al peso della sua riga (720 per il titolo, 470 per il cliente…). La riga si compone dalla squadra verso sinistra nell’ordine di lettura, così la forma è lo specchio della pagina; se una riga non sta nella giustezza, la dimensione scende finché ci sta. Quello che resta fino al cursore si riempie di quadrati, blocchetti più bassi senza lettera.

Un blocchetto di piombo in CSS

Ogni carattere è un div con due facce: l’occhio in alto, alto 22 pixel, con un gradiente inclinato verso la luce e un bordo chiaro in alto a sinistra, e il fusto davanti, più scuro, con la tacca disegnata da due righe di gradiente e un’ombra. La lettera sta sull’occhio con scaleX(-1), in rilievo grazie a tre text-shadow (luce in alto a sinistra, ombra in basso a destra, ombra morbida). Ogni carattere ha una luminosità sua, fra 0,9 e 1,08, come i caratteri consumati; il riflesso della serrata è un ::after in soft-light che scorre grazie a una variabile CSS animata da GSAP.

Tutto il lavoro in una timeline

Una sola timeline GSAP tiene tutto: le cadute (ognuna gira verso dritto con power2.in e finisce un filo larga), il sobbalzo della riga con elastic, i quadrati, la serrata, il viaggio della riga nel vantaggio con un rimbalzo, il rullo, la platina, il foglio. La percentuale viene da callback messe nel momento in cui ogni carattere tocca il fondo. Nella generazione che va storta il carattere sbagliato entra nella timeline prima di quello giusto, seguito da un addPause: la timeline si ferma da sola, e Retry la fa ripartire.

Il rullo e il torchio

Il rullo scende sul vantaggio con power1.inOut, e il momento in cui il suo centro passa su ogni riga si trova invertendo la curva: lì la riga prende la classe che fa nere le lettere. La platina scende da più grande e più in alto (più vicina all’occhio) con power3.in, e all’arrivo tutta la scena scende di due pixel e torna su con una molla. Il foglio ha due facce in CSS 3D con backface-visibility: girarlo di 180 gradi sull’asse verticale rovescia di nuovo lo specchio, così la stampa si legge dritta; l’ombra sotto si stringe e si riallarga mentre gira.

La stampa su canvas

La pagina si dipinge una volta per lavoro, dalle stesse larghezze dei caratteri specchiate intorno al centro: la carta (grana pixel per pixel su un canvas a parte, qualche fibra, un’ombra ai bordi), il solco dell’impressione (la lettera in chiaro spostata in basso a destra e in scuro in alto a sinistra), poi l’inchiostro su un canvas suo, con un’opacità diversa per lettera, il bordo spremuto dato da un contorno sottile e migliaia di puntini tolti con destination-out, unito alla carta in multiply. Il dorso è la stessa carta con le lettere a rovescio appena visibili.

Valori da cambiare

NomeFileDi partenzaCosa cambia
SECONDSscript.js6Quanto dura la composizione delle righe, in secondi; l’inchiostrazione e la stampa aggiungono circa due secondi (una vera generazione segue il lavoro del server).
LINESscript.js['INVOICE INV-0412', 'Marlow & Finch', 'Due 30 October', 'Total €1,840.00']Le righe della fattura, al massimo quattro: una riga troppo lunga per il compositoio rimpicciolisce da sola.
WEIGHTSscript.js[720, 470, 420, 640]Il peso del carattere di ogni riga, da 100 a 900: il carattere è variabile, quindi ogni peso è vero, anche nella stampa.
FAIL_EVERYscript.js2Nella demo, una generazione ogni quante va storta (0 mai).
FAIL_LINEscript.js3In quale riga cade il carattere sbagliato.
FAIL_SORTscript.js['b', 'd']Il carattere giusto e quello sbagliato che cade al suo posto; se nella riga non c’è il giusto, sbaglia una lettera a metà riga.
LABELSscript.js{ busy: 'Generating…', done: 'Download PDF', saved: 'Downloaded', retry: 'Retry' }Le scritte del pulsante mentre lavora, alla fine, dopo lo scaricamento e dopo l’errore; quella a riposo, Generate invoice, si cambia in index.html.
WORDSscript.js{ set: 'Laying out INV-0412', render: 'Rendering PDF', done: 'Invoice ready', fail: 'Glyph mismatch on line' }La riga sotto il disegno mentre compone (con la percentuale), mentre stampa, alla fine e dopo l’errore (seguita dal numero della riga); quella a riposo, Ready to generate, si cambia in index.html.

Provali nel Playground

Quando usarla

Per un’attesa di qualche secondo che finisce con un documento: generare una fattura, un preventivo, una ricevuta o un contratto in PDF, esportare un report impaginato, preparare un file da stampare. Sta bene in un gestionale per freelance e studi, in un’app di fatturazione o di editoria, dove la tipografia fa parte del prodotto. Per un’attesa di un attimo è troppo lungo.

Sul telefono

Il disegno è largo 360 pixel e si scala alla larghezza che ha: a 390 resta intero con 16 pixel per lato, e le lettere restano nitide perché sono testo vero; il canvas della stampa dipinge almeno a due volte la densità. Sopra c’è spazio per i caratteri che cadono. Le pillole sono alte 44 e col dito si schiacciano e rimbalzano; il carattere che aspetta sopra la squadra c’è col mouse e con la tastiera.

Con il movimento ridotto

Niente cade e niente si sposta: ogni riga compare in dissolvenza già al suo posto nel vantaggio, una dopo l’altra, le lettere diventano nere riga per riga senza rullo, e alla fine il foglio stampato compare in dissolvenza già dritto, senza torchio e senza giro. Il carattere sbagliato si colora di rosso e svanisce. Percentuale, parole e spunta si aggiornano con dissolvenze.

Trappole

  • La grana della carta fatta con getImageData sul canvas della pagina faceva protestare Chrome (letture ripetute): ora la grana nasce su un canvas a parte con createImageData e si posa sopra con drawImage.
  • Il primo carattere che aspetta al passaggio del mouse, messo appena sopra la squadra, spariva quasi dietro di lei e si vedeva solo una scheggia: ora sta più in alto e un poco a sinistra, inclinato, ben staccato dall’acciaio.
  • Con pochi puntini tolti la stampa sembrava un testo del computer su carta beige: i puntini sono quasi il doppio, e insieme all’opacità diversa per lettera e al bordo spremuto la pagina ha l’aria dell’inchiostro vero.
  • La riga che scende nel vantaggio deve passare sopra il bordo del compositoio, che invece copre il fondo dei caratteri finché sono lì: il contenitore delle righe non ha z-index (altrimenti chiuderebbe tutte le righe in un livello solo) e ogni riga cambia il suo durante il viaggio.

Librerie

GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Instrument Sans (font variabile) 5.3.0.

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

Collaudo

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