Vai al contenuto
animorio
EN
Tutti i componenti

Il campo a macchina da scrivere

Moduli, Testo, SVGGSAP, CanvasImpegnativaPeso compresso: Demo 18 KBGSAP 28 KBGratisScarica lo zip

Il campo email di un’iscrizione battuto a macchina su una striscia di carta avvolta sul rullo: il punto di battuta resta fermo e a ogni tasto è la carta a spostarsi di uno scatto, mentre una leva sale dal basso e lascia la lettera inchiostrata in modo sempre un po’ diverso. Il tasto indietro toglie la lettera col nastro correttore, vicino al margine trema il campanello, e inviando la leva di ritorno riporta indietro il carrello, il rullo gira di una riga e la macchina scrive la risposta da sola.

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 typewriter field</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/courier-prime@5.3.0/400.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/courier-prime@5.3.0/400-italic.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/courier-prime@5.3.0/700.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the newsletter sign-up of a short fiction magazine, typed on a strip of paper wound on a black rubber platen:
         the strike point stays still behind the metal guide and the paper moves, the return lever throws the carriage
         back when the address is sent, the knurled knob turns with the platen and the bell shakes near the margin -->
    <form class="tw" action="#" novalidate data-tw data-autoclic>
      <div class="tw__top">
        <label class="tw__label" for="tw-input">Email address</label>
      </div>
      <svg class="tw__bell" viewBox="0 0 28 22" aria-hidden="true" data-bell>
        <g data-bell-body>
          <path class="bell__dome" d="M8 16.2c0-4.6 2.6-7.6 6-7.6s6 3 6 7.6z"/>
          <path class="bell__base" d="M6.4 16.6h15.2"/>
          <path class="bell__stem" d="M14 8.6V6.4"/>
          <circle class="bell__knob" cx="14" cy="5.4" r="1.4"/>
        </g>
        <path class="bell__ring" d="M5.4 8.2c-1.3 1.5-1.8 3.4-1.5 5.4M2.6 6.4C.8 8.6.1 11.3.6 14" data-ring/>
        <path class="bell__ring" d="M22.6 8.2c1.3 1.5 1.8 3.4 1.5 5.4M25.4 6.4c1.8 2.2 2.5 4.9 2 7.6" data-ring/>
      </svg>
      <div class="tw__field" data-field>
        <svg class="tw__lever" viewBox="0 0 34 40" aria-hidden="true" data-lever>
          <defs>
            <linearGradient id="tw-chrome" x1="0" y1="0" x2="1" y2="0.35">
              <stop offset="0" stop-color="#8a8e95"/>
              <stop offset="0.28" stop-color="#ffffff"/>
              <stop offset="0.55" stop-color="#a4a8af"/>
              <stop offset="0.78" stop-color="#e9ebee"/>
              <stop offset="1" stop-color="#5d6168"/>
            </linearGradient>
          </defs>
          <g data-lever-arm>
            <path class="lever__arm" d="M12.4 34C12.3 25.6 9.8 17 5.2 10.2l3.6-2.4C13.4 14.8 17 24 17.6 34z"/>
            <path class="lever__blade" d="M.9 8.8C.2 6.2 1.7 2.9 5 1.4 8.1 0 10.6.9 10.9 3c.3 1.9-1.2 3.9-3.3 5.3-2.4 1.6-5.9 2.6-6.7.5z"/>
            <path class="lever__shine" d="M2.6 6.6c.9-1.9 2.6-3.3 4.7-3.8"/>
          </g>
          <circle class="lever__boss" cx="15" cy="34" r="4.4"/>
          <circle class="lever__screw" cx="15" cy="34" r="1.3"/>
        </svg>
        <svg class="tw__knob" viewBox="0 0 16 50" preserveAspectRatio="none" aria-hidden="true">
          <defs>
            <linearGradient id="tw-knob" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0" stop-color="#000" stop-opacity="0.85"/>
              <stop offset="0.3" stop-color="#000" stop-opacity="0"/>
              <stop offset="0.42" stop-color="#fff" stop-opacity="0.07"/>
              <stop offset="0.7" stop-color="#000" stop-opacity="0"/>
              <stop offset="1" stop-color="#000" stop-opacity="0.9"/>
            </linearGradient>
          </defs>
          <rect class="knob__body" x="0" y="0" width="16" height="50" rx="4.5"/>
          <path class="knob__ridges" data-ridges/>
          <path class="knob__grooves" data-grooves/>
          <rect x="0" y="0" width="16" height="50" rx="4.5" fill="url(#tw-knob)"/>
        </svg>
        <div class="tw__window" data-window>
          <div class="tw__sheet" data-sheet>
            <div class="tw__paper" data-paper></div>
            <div class="tw__lines" aria-hidden="true" data-lines>
              <span class="tw__ph" data-ph></span>
            </div>
            <input class="tw__input" id="tw-input" name="email" type="text" inputmode="email" autocomplete="email" autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="send" maxlength="254" aria-describedby="tw-help" aria-invalid="false" data-input>
          </div>
          <div class="tw__shade" aria-hidden="true"></div>
          <div class="tw__guide" aria-hidden="true" data-guide>
            <svg class="guide__plate" viewBox="-32 0 64 60">
              <defs>
                <linearGradient id="tw-plate" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0" stop-color="#f6f7f8"/>
                  <stop offset="0.35" stop-color="#c3c7cd"/>
                  <stop offset="0.7" stop-color="#8b9098"/>
                  <stop offset="1" stop-color="#5c6067"/>
                </linearGradient>
              </defs>
              <path class="guide__index" d="M-3.4 7.6h6.8L0 12.2z" data-index/>
              <path class="guide__body" d="M-27 44.6c0-.9.7-1.6 1.6-1.6h21.6L0 47.4 3.8 43h21.6c.9 0 1.6.7 1.6 1.6v9.8H-27z"/>
              <path class="guide__slots" d="M-15.5 46.6v4.6M15.5 46.6v4.6"/>
              <path class="guide__lip" d="M-26 43.6h21.9M4.1 43.6H26"/>
            </svg>
            <span class="tw__tape" data-tape></span>
            <span class="tw__bar" data-bar></span>
          </div>
        </div>
        <button class="tw__key" type="submit" data-key>
          <span class="key__top">
            <span class="key__cap">
              <span class="key__word" data-key-word>Subscribe</span>
              <svg class="key__tick" viewBox="0 0 24 24" aria-hidden="true" data-key-tick><path d="M6.4 12.6l3.8 3.8 7.6-8.6"/></svg>
            </span>
          </span>
        </button>
      </div>
      <div class="tw__help" aria-hidden="true" data-help><span class="help__line is-help">A new short story every other Sunday.</span></div>
      <span class="sr" id="tw-help" data-help-sr></span>
      <span class="sr" aria-live="polite" data-live></span>
    </form>
  </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.

24 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 campo a macchina da scrivere» da Animorio e adattala al mio sito

Cosa fa

Su un fondo bianco la scritta Email address e, sotto, il carrello nero lucido di una macchina da scrivere: a sinistra la manopola zigrinata del rullo con la leva di ritorno cromata che spunta in alto, in mezzo una striscia di carta color avorio sul rullo di gomma nera (la curva del rullo si vede sopra e sotto la carta, con un riflesso morbido), sopra la carta una piccola guida di metallo con una tacca a V che segna il punto di battuta, e a destra un tasto avorio con la ghiera cromata, Subscribe. Sotto, in grigio, A new short story every other Sunday. A riposo la carta mostra a matita you@example.com, che finisce proprio alla tacca, e a sinistra il bordo del foglio. Toccato il campo, intorno al carrello compare un anello d’inchiostro e il cursore lampeggia alla tacca. A ogni tasto il martelletto sale da sotto la guida, batte, la lettera resta stampata un po’ più scura e poi si asciuga, e la carta scatta a sinistra di uno spazio con un piccolo sobbalzo: le lettere hanno inchiostro diverso, stanno un poco più su o più giù, pendono appena, sono macchiate dove il nastro ha preso meno e ogni tanto battute due volte. Lo spazio fa avanzare il carrello senza battere. Il tasto indietro riporta il carrello di uno spazio, sale una striscia di nastro correttore bianco e la lettera si stacca arricciandosi verso l’alto e sparisce. Un indirizzo incollato o riempito dal browser si batte da solo, lettera dopo lettera, veloce. Sei lettere prima del margine, sopra la tacca compare un campanello che oscilla con due archetti ai lati, senza suono. Un indirizzo più lungo della carta scorre via a sinistra, e portando il cursore all’inizio il carrello torna indietro a mostrarlo. Premendo Invio o Subscribe con un indirizzo sbagliato il carrello trema, l’anello e l’indice in alto diventano rossi e sotto il campo si batte in rosso cosa manca, per esempio Add the ending, like .com; appena l’indirizzo è giusto le lettere rosse si staccano e torna la riga grigia. Con un indirizzo giusto la leva di ritorno scatta, il carrello corre indietro fino al margine e ci sbatte, il rullo gira portando la riga in alto, schiacciata dalla curva, mentre la manopola gira con lui; poi la macchina batte da sola You’re on the list., sul tasto si disegna una spunta e sotto si batte The first story comes on Sunday. Un clic sulla carta o un tasto fa girare il rullo all’indietro e riporta l’indirizzo, per correggerlo.

Come funziona

Il campo vero viaggia con la carta

L’input è vero (label, autocomplete email, inputmode email per la tastiera con la @, enterkeyhint send, aria-invalid e aria-describedby verso una riga per i lettori di schermo, una regione aria-live per errori e risposta). Sta dentro il foglio che si sposta, largo migliaia di pixel così non scorre mai da sé, con le lettere trasparenti e il cursore visibile: le sue lettere cadono esattamente sotto quelle disegnate (un carattere monospaziato misurato con measureText), quindi il suo cursore, la selezione, l’autocompletamento e la tastiera del telefono funzionano dove si vedono.

La coda della macchina

A ogni cambio il valore si confronta col testo disegnato (stesso inizio, stessa fine). Quello che si aggiunge in fondo entra in una coda di battute, quello che si toglie in fondo in una coda di correzioni; ogni passo è una timeline di GSAP: il martelletto sale con power3.in, all’urto la lettera si inchiostra e il carrello avanza della sua larghezza, con un sobbalzo e un colpo del rullo che proseguono da soli. Se si accumulano passi la mano accelera fino a PASTE secondi a lettera. Un cambio in mezzo alla riga (la macchina non saprebbe farlo) scioglie la coda e le lettere si spostano al loro posto.

L’inchiostro

Una volta sola, su un canvas, si disegna un rumore a valori (macchie grandi quanto una lettera, una chiazzatura più fine, la trama obliqua del nastro e qualche fibra senza inchiostro) che diventa la maschera alfa di ogni lettera, presa da un punto diverso. Con mask-composite: intersect le si somma un gradiente con un angolo a caso, perché il martelletto non batte mai piatto. In più ogni lettera ha la sua densità, uno scarto sulla riga di JITTER pixel, un’inclinazione di TILT gradi e a volte una seconda battuta leggera in text-shadow; INK regola quanto è irregolare tutto.

Il rullo e il ritorno

Una riga sta sul cilindro del rullo: sale del raggio per il seno del suo angolo e si schiaccia del coseno, e si spegne girando via. Inviando, la leva ruota attorno al suo perno, il carrello corre al margine con power2.in e ci sbatte, e il rullo gira di una riga portando su l’indirizzo e giù la riga nuova; le scanalature della manopola, linee intorno a un cilindro visto di fronte, si ridisegnano solo mentre gira. Dopo l’attesa di subscribe() la risposta si batte da sola; un clic o un tasto fa girare il rullo al contrario.

Errori, campanello e anteprima

Il controllo dice cosa manca e viene battuto sotto il campo lettera per lettera in rosso, con la stessa maschera d’inchiostro; il carrello trema e l’indice della guida diventa rosso. Quando il carrello passa BELL lettere prima del margine il campanello oscilla una volta per riga. Un clic da script sul modulo (detail 0, l’anteprima della libreria) batte da solo un indirizzo con un errore corretto, lo invia, poi uno senza finale, rifiutato in rosso, completato e inviato.

Valori da cambiare

NomeFileDi partenzaCosa cambia
PLACEHOLDERscript.js'you@example.com'L’indirizzo scritto a matita sulla carta finché il campo è vuoto.
DONEscript.js'You’re on the list.'Quello che la macchina batte da sola sulla riga nuova quando l’indirizzo è partito.
NOTEscript.js'The first story comes on Sunday.'La riga battuta sotto il campo quando l’indirizzo è partito.
STRIKEscript.js0.06I secondi che il martelletto impiega a salire e battere sulla carta.
CARRIAGEscript.js0.07I secondi che il carrello impiega a scattare di uno spazio.
JOLTscript.js1.2Di quanti pixel il carrello va oltre a ogni scatto (e quanto sobbalza il rullo).
INKscript.js0.45Quanto è irregolare l’inchiostro: 0 tutte le lettere uguali, 1 macchiate e sbiadite.
JITTERscript.js0.6Di quanti pixel una lettera può uscire dalla riga, in su o in giù.
TILTscript.js0.6Di quanti gradi una lettera può pendere.
PASTEscript.js0.035I secondi fra due battute quando un indirizzo intero è incollato o riempito dal browser.
BELLscript.js6Quante lettere prima del margine oscilla il campanello.
WAITscript.js0.5Quanti secondi dura l’iscrizione nella demo, prima che la risposta venga battuta.

Provali nel Playground

Quando usarla

Nell’iscrizione alla newsletter di una rivista, di uno scrittore, di una casa editrice, di una libreria o di un sito di racconti: dove lo scrivere è il prodotto e un campo qualsiasi sarebbe un’occasione persa. Resta un campo email vero, con l’autocompletamento del browser e la tastiera del telefono.

Sul telefono

Su un telefono di 390 pixel il carrello è largo quanto lo schermo, resta alto 60, le lettere scendono a 16 pixel (così iOS non ingrandisce la pagina toccando il campo) e il tasto si stringe a 86. Un tocco apre la tastiera per le email col tasto Invio che dice Send; la carta, la leva e il campanello sono gli stessi, con meno lettere prima del margine.

Con il movimento ridotto

Niente martelletto, niente scatti né sobbalzi: la carta si sposta subito di uno spazio e la lettera compare in dissolvenza al suo posto, e se ne va allo stesso modo. Inviando, la riga svanisce e la risposta compare lettera per lettera senza leva né rullo che gira; il carrello non trema, il campanello compare e svanisce fermo, le righe sotto il campo si scambiano in dissolvenza.

Trappole

  • Con type email il campo non dice dov’è il cursore e toglie gli spazi in fondo al valore: il testo del campo e quello disegnato, messi lettera per lettera uno sull’altro, non avrebbero più combaciato. Il campo è di tipo text con inputmode email, che sul telefono dà la stessa tastiera.
  • Le lettere sembravano un Courier digitale: una maschera di rumore sola era troppo leggera. Ora la maschera ha anche la puntinatura del nastro e qualche fibra vuota, e le si somma con mask-composite un gradiente a caso per lettera, il lato battuto meno forte.
  • Ogni passo della coda durava quanto i suoi ornamenti (la leva che torna con elastic.out, i sobbalzi dopo lo scatto): la risposta arrivava tardi e chi scriveva in fretta accumulava ritardo. Ora un passo finisce quando il carrello si posa, e il resto prosegue da solo.
  • Il riflesso sul bordo della carta stava su uno strato fermo e si vedeva anche sulla gomma nuda a sinistra del foglio: ora sta sulla carta, che si sposta.
  • La riga nuova sotto il campo entrava mentre le ultime lettere di quella battuta si staccavano ancora, e due righe si sovrapponevano: ora aspetta l’ultima.
  • Riavvolgendo la carta per correggere, la riga della risposta passava dietro la guida ancora scura: scendendo si spegne prima che salendo.
  • Riportato l’indirizzo, il campanello suonava di nuovo al tasto dopo: ora ricorda se la riga ha già passato il segno.

Librerie

GSAP 3.15.0, Courier Prime 5.3.0.

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

Collaudo

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