Vai al contenuto
animorio
EN
Tutti i componenti

Il menu a scatola a molla

Menu, Fisica, SVG, HoverGSAP, Solo CSSImpegnativaPeso compresso: Demo 14 KBGSAP 28 KBGratisScarica lo zip

Il menu di un negozio di giocattoli come una scatola a molla di latta litografata: chiusa è il pulsante, e sotto il puntatore la manovella gira da sola e il coperchio si solleva appena, come se dentro qualcosa spingesse. Premuta, la manovella fa un giro intero mentre la scatola trema, il coperchio si spalanca contro il fermo e il cartello del menu salta fuori su una molla d'acciaio: esce stretto dalla bocca della scatola, si allarga, va oltre, si allunga e dondola sulla molla, con la gorgiera a pieghe sotto. La molla è una spirale ridisegnata a ogni fotogramma lungo un asse che si piega. Il puntatore che passa su una voce dà uno schiaffetto al cartello, che si può anche prendere e lanciare; scelta una voce, il cartello viene spinto giù nella scatola e il coperchio si chiude di colpo.

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 jack-in-the-box menu</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the menu of a toy shop as a lithographed tin jack-in-the-box. Closed, the box is the button: under the pointer
         its crank turns a little and the lid lifts a crack, as if something inside were pushing. Pressed, the crank
         winds a full turn, the lid bangs open and the menu card shoots out on a steel spring, overshoots, and wobbles
         on it. The spring is a coil drawn again every frame along a bent axis; the card can be grabbed and flicked.
         Choosing a section pushes the card back into the box and the lid snaps shut. -->
    <nav class="jack" aria-label="Toy shop" data-jack>
      <div class="jack__frame" data-frame>
        <!-- behind: the shadow on the table, the inside of the box and the lid, drawn by the script -->
        <svg class="jack__back" data-back aria-hidden="true">
          <defs>
            <radialGradient id="jack-shadow" cx="50%" cy="50%" r="50%">
              <stop offset="0" stop-color="#1b0f0a" stop-opacity="0.26"/>
              <stop offset="1" stop-color="#1b0f0a" stop-opacity="0"/>
            </radialGradient>
            <linearGradient id="jack-inside" x1="0" y1="0" x2="0.3" y2="1">
              <stop offset="0" class="stop-inside-lit"/>
              <stop offset="1" class="stop-inside"/>
            </linearGradient>
            <linearGradient id="jack-lining" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0" class="stop-lining-lit"/>
              <stop offset="0.55" class="stop-lining"/>
              <stop offset="1" class="stop-lining-dark"/>
            </linearGradient>
          </defs>
          <ellipse class="jack__shadow" data-shadow fill="url(#jack-shadow)"/>
          <path class="box-inside" data-inside fill="url(#jack-inside)"/>
          <path class="box-rim" data-rim/>
          <path class="lid-under" data-lid-under fill="url(#jack-lining)"/>
          <path class="lid-top" data-lid-top/>
          <path class="lid-lip" data-lid-lip/>
          <path class="lid-line" data-lid-line/>
        </svg>

        <!-- the jack: the spring and the card. Everything under the front edge of the box is inside it, so hidden -->
        <div class="jack__out" data-out>
          <svg class="spring" data-spring-svg aria-hidden="true">
            <path class="spring__back" data-coil-back/>
            <path class="spring__edge" data-coil-edge/>
            <path class="spring__front" data-coil-front/>
            <path class="spring__shine" data-coil-shine/>
          </svg>

          <div class="card" data-card>
            <div class="card__body">
              <ul class="card__list" id="jack-menu" data-list inert>
                <li><a class="item" href="#shop" data-item><span class="item__label">Shop toys</span></a></li>
                <li><a class="item" href="#wind-ups" data-item><span class="item__label">Wind-ups</span></a></li>
                <li><a class="item" href="#puppets" data-item><span class="item__label">Puppets</span></a></li>
                <li><a class="item" href="#gift-cards" data-item><span class="item__label">Gift cards</span></a></li>
                <li><a class="item" href="#visit" data-item><span class="item__label">Visit us</span></a></li>
              </ul>
            </div>
            <!-- the pleated ruff round the jack's neck, where the spring holds the card -->
            <svg class="ruff" viewBox="0 0 120 30" aria-hidden="true" data-ruff></svg>
          </div>
        </div>

        <!-- in front: the tin box, which is the button -->
        <button class="box" type="button" aria-expanded="false" aria-controls="jack-menu" data-box data-autoclic>
          <svg class="box__art" data-box-art aria-hidden="true">
            <defs>
              <pattern id="jack-diamonds" width="28" height="36" patternUnits="userSpaceOnUse">
                <rect width="28" height="36" class="tin-red"/>
                <path d="M14 0 L28 18 L14 36 L0 18 Z" class="tin-red-dark"/>
              </pattern>
              <pattern id="jack-stripes" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="skewY(-46.7)">
                <rect width="12" height="12" class="tin-blue"/>
                <rect width="6" height="12" class="tin-blue-dark"/>
              </pattern>
              <linearGradient id="jack-side-shade" x1="0" y1="0" x2="1" y2="0">
                <stop offset="0" stop-color="#000" stop-opacity="0.08"/>
                <stop offset="1" stop-color="#000" stop-opacity="0.34"/>
              </linearGradient>
              <linearGradient id="jack-front-shade" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0" stop-color="#fff" stop-opacity="0.18"/>
                <stop offset="0.4" stop-color="#fff" stop-opacity="0"/>
                <stop offset="1" stop-color="#000" stop-opacity="0.16"/>
              </linearGradient>
            </defs>
            <g data-faces></g>
            <g data-crank></g>
          </svg>
          <span class="box__plate" aria-hidden="true">
            <span class="box__words" data-words><span class="box__word">Menu</span><span class="box__word">Close</span></span>
          </span>
          <span class="sr" data-name>Open the menu</span>
        </button>
      </div>
      <p class="sr" aria-live="polite" data-live></p>
    </nav>
  </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.

36 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 menu a scatola a molla» da Animorio e adattala al mio sito

Cosa fa

Su un fondo bianco c'è una scatola di latta vista un po' dall'alto e da destra: davanti rossa a rombi, il fianco blu a righe, il coperchio giallo, le fasce gialle in alto e ai piedi, la manovella con il pomello di legno sul fianco e una targhetta color crema con Menu. È il pulsante. Col puntatore sopra la manovella gira piano da sola e il coperchio si solleva di un dito e ricade sulla sua molla. Premendo, la scritta della targhetta rotola e diventa Close, la manovella fa un giro intero accelerando e la scatola trema sempre di più; poi il coperchio si spalanca, sbatte contro il fermo e rimbalza, e solo quando il suo bordo è passato dietro il cartello il cartello parte. Esce dalla bocca della scatola stretto, si allarga mentre ne esce, sale più in alto di dove si fermerà, si allunga nella corsa e torna giù sulla molla d'acciaio, che si vede uscire dalla scatola con le spire davanti chiare e quelle dietro scure. Appena fuori dal bordo il cartello prende una spinta di lato e dondola, con la molla che si piega sotto di lui e il cartello che fa un piccolo cenno in ritardo, come una testa. Sul cartello ci sono le cinque voci del negozio (Shop toys, Wind-ups, Puppets, Gift cards, Visit us) e quella in cui sei dice You're here. Passando con il puntatore su una voce, le sue lettere saltano una dopo l'altra e ricadono con un piccolo rimbalzo, e il cartello prende uno schiaffetto nella direzione in cui va la mano. Il cartello si può prendere e tirare: è legato a un elastico, e lasciato torna a dondolare. Scegliendo una voce le sue lettere saltano più in alto, poi il cartello viene spinto dritto dentro la scatola, il coperchio si chiude di colpo, la scatola sobbalza e la targhetta torna a dire Menu.

Come funziona

Una scatola in proiezione obliqua

Tutto è disegnato in unità, con un punto (x, y, z) che finisce sullo schermo in (x + z·0,32, y − z·0,34): così si vedono il davanti, il fianco destro e il sopra senza una vera prospettiva. Le facce della scatola sono SVG disegnati una volta, con i rombi e le righe come pattern (quello del fianco piegato con skewY dello stesso angolo del fianco). Il coperchio invece è ridisegnato dallo script: ogni suo punto gira attorno alla cerniera dietro, e oltre i 19 gradi si vede la sua faccia di dentro, di latta nuda, perché è lì che la sua normale smette di guardare chi guarda.

Dentro la scatola, tagliato al bordo

La molla e il cartello stanno in uno strato con un clip-path che taglia tutto sotto il bordo davanti della scatola: in proiezione obliqua, un punto a metà profondità sotto il piano del coperchio si vede dalla bocca solo se sta sopra quella linea, quindi il taglio è esatto. Il cartello è più largo della bocca: mentre esce è stretto all'80 per cento e si allarga quando la gorgiera passa il bordo. Il cartello parte solo quando il coperchio ha passato i 62 gradi, cioè quando il suo bordo davanti è finito dietro al cartello: prima ci passerebbe attraverso.

La molla che lancia

L'altezza del cartello sopra il bordo è su una molla smorzata (POP) con la velocità tagliata a LAUNCH: la forza enorme della molla compressa lo porta subito alla velocità massima, lui corre dritto fuori dalla scatola e intorno alla posizione d'arrivo la molla lo frena, lo manda oltre di circa LAUNCH diviso la pulsazione e lo riporta giù. La velocità allunga il cartello in verticale e lo stringe in larghezza di quel tanto che ne tiene l'area. Per richiuderlo la molla si spegne e il cartello cade con un'accelerazione fissa, con la molla di lato molto più rigida perché entri dritto.

Dondolo e cenno

Lo spostamento di lato della cima della molla è un altro oscillatore poco smorzato (WOBBLE), e il cartello si inclina di LEAN gradi per unità come la punta di una molla piegata. In più il cartello ha un suo angolo, il cenno, su una molla rapida spinta dall'accelerazione della cima al contrario: quando la cima scatta a destra il cartello resta indietro e poi la raggiunge. Il puntatore che entra in una voce dà alla cima una velocità proporzionale alla sua (BAT); preso con la mano, il cartello segue il puntatore su un elastico (una tangente iperbolica) e lasciato riparte con la velocità della mano.

La spirale ridisegnata

L'asse della molla è una curva di Bézier cubica che parte dritta dal fondo della scatola e arriva alla gorgiera inclinata col cartello. Lungo l'asse lo script mette 26 punti per spira: ogni punto si sposta di lato del raggio per il seno dell'angolo, e lungo l'asse del raggio per il coseno per 0,3, perché ogni spira, vista un po' dall'alto, è un'ellisse col lato vicino più in basso. Il segno del coseno dice se il filo passa davanti o dietro: i tratti dietro vanno in un path scuro sotto, quelli davanti in tre path (bordo scuro, acciaio, riflesso) sopra. Niente si ridisegna se cartello e molla non si sono mossi.

Valori da cambiare

NomeFileDi partenzaCosa cambia
POPscript.js[210, 0.2]La molla su cui esce il cartello: quanto tira verso la sua posizione, e quanto è smorzata (1 = nessun rimbalzo).
LAUNCHscript.js1500La velocità massima del cartello che salta fuori, in unità al secondo: più è alta, più va oltre.
WOBBLEscript.js[46, 0.09]Il dondolo del cartello da una parte all'altra sulla molla: rigidità e smorzamento.
LEANscript.js0.38Quanto si inclina il cartello mentre dondola, in gradi per unità di spostamento.
WINDscript.js0.42I secondi in cui la manovella gira prima che il coperchio si spalanchi.
LIDscript.js[190, 0.24]Il coperchio che si spalanca e si richiude: rigidità e smorzamento.
BATscript.js1Quanto forte il puntatore che passa su una voce spinge il cartello.
HOPscript.js0.3Quanto saltano le lettere di una voce sotto il puntatore, in parti della loro altezza.

Provali nel Playground

Quando usarla

Per il menu di un sito che vuole far sorridere: un negozio di giocattoli, una festa per bambini, un circo, una pasticceria, un'app di giochi, una pagina di sorprese o di regali. Da tre a sei voci brevi (una dozzina di lettere al massimo), che stanno sul cartello una sotto l'altra.

Sul telefono

Tutto il disegno si scala sulla larghezza e sull'altezza dello schermo: a 390 pixel il cartello è largo circa 215 e la scatola aperta ci sta senza sforare. Al tocco non c'è la manovella che gira da sola: il tocco apre subito. Il cartello si prende e si lancia anche col dito, e il tocco su una voce la sceglie.

Con il movimento ridotto

Niente salti né dondoli: premendo, il coperchio è già aperto e il cartello compare al suo posto in dissolvenza; chiudendo sparisce in dissolvenza e il coperchio è già chiuso. La manovella non gira, le lettere non saltano e il cartello non si prende con la mano; la voce sotto il puntatore cambia solo colore.

Trappole

  • Il cartello partiva insieme al coperchio e, nei primi fotogrammi, gli passava attraverso: il bordo davanti del coperchio sta davanti al cartello finché l'angolo è sotto i 60 gradi. Il cartello aspetta in uno stato a parte finché il coperchio non ha passato i 62 gradi.
  • La spinta di lato data al lancio faceva uscire il cartello di sbieco dalla bocca, ruotato intorno a un punto ancora dentro la scatola: la spinta arriva quando la gorgiera passa il bordo, e l'inclinazione arriva piena solo dopo le prime 40 unità fuori dalla scatola.
  • Close stava sotto la targhetta con un translateY(110%) nel CSS, e GSAP, leggendolo come pixel, ci aggiungeva il suo yPercent: la parola restava fuori. All'avvio lo script la riprende con y: 0 e yPercent: 110.
  • Il dritto del coperchio si sceglieva con tan(angolo) minore di 0,34, vero anche oltre i 90 gradi dove la tangente è negativa: si vedeva il giallo di fuori invece della latta di dentro. Ora si confronta l'angolo con arctan(0,34).

Librerie

GSAP 3.15.0, Fraunces (font variabile) 5.3.0.

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

Collaudo

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