Vai al contenuto
animorio
EN
Tutti i componenti

Il menu a sipario

Menu, Fisica, WebGL, TransizioniWebGL, GSAPImpegnativaPeso compresso: Demo 14 KBGSAP 28 KBGratisScarica lo zip

Il menu di un cinema sta su un piccolo palco dietro un sipario di velluto rosso: premendo Menu le due tende scorrono sul binario, vengono legate ai lati con le pieghe che ondeggiano e sotto un occhio di bue compaiono le voci. Il sipario è una stoffa simulata davvero, punti e fili in WebGL, e col puntatore vicino il suo bordo si scosta e si increspa.

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 curtain menu</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bodoni-moda@5.3.0/opsz.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bodoni-moda@5.3.0/opsz-italic.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/hanken-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the curtain menu: the button that opens and closes it, and the stage in its gilded arch, closed by the
         curtain, with the menu standing behind it -->
    <div class="theatre" data-theatre>
      <div class="bar">
        <button class="toggle" type="button" aria-expanded="false" aria-controls="programme" data-toggle data-autoclic>
          <svg class="toggle__icon" viewBox="0 0 24 24" aria-hidden="true">
            <path class="toggle__rail" d="M2.5 3.5h19"/>
            <path class="toggle__drape toggle__drape--left" d="M3.5 4h8.2c-.4 5.6-2.6 11.3-8.2 16.2z"/>
            <path class="toggle__drape toggle__drape--right" d="M20.5 4h-8.2c.4 5.6 2.6 11.3 8.2 16.2z"/>
          </svg>
          <span class="toggle__words" aria-hidden="true"><span class="toggle__word" data-word>Menu</span></span>
          <span class="sr" data-name>Open the menu</span>
        </button>
      </div>

      <!-- the stage: the menu stands on it, behind the curtains. The curtains and the valance over them are a cloth
           simulated in WebGL on the canvas, which lets the pointer through to the menu once they are open -->
      <div class="frame">
        <div class="scene" data-stage>
          <div class="set" aria-hidden="true">
            <span class="set__spot" data-spot></span>
            <span class="set__floor"><svg viewBox="0 0 100 100" preserveAspectRatio="none"><path d="M6.8 0L-2.8 100M10.4 0L1.6 100M14.0 0L6.0 100M17.6 0L10.4 100M21.2 0L14.8 100M24.8 0L19.2 100M28.4 0L23.6 100M32.0 0L28.0 100M35.6 0L32.4 100M39.2 0L36.8 100M42.8 0L41.2 100M46.4 0L45.6 100M50.0 0L50.0 100M53.6 0L54.4 100M57.2 0L58.8 100M60.8 0L63.2 100M64.4 0L67.6 100M68.0 0L72.0 100M71.6 0L76.4 100M75.2 0L80.8 100M78.8 0L85.2 100M82.4 0L89.6 100M86.0 0L94.0 100M89.6 0L98.4 100M93.2 0L102.8 100"/></svg></span>
          </div>

          <nav class="programme" id="programme" aria-label="Main" data-menu inert>
            <ul class="programme__list">
              <li><a class="act" href="#" data-link><span class="act__name">What’s on</span><span class="act__note">Eighteen nights in October</span></a></li>
              <li><a class="act" href="#" data-link><span class="act__name">Films</span><span class="act__note">On 35 and 70 mm</span></a></li>
              <li><a class="act" href="#" data-link><span class="act__name">Supper club</span><span class="act__note">Tables from 6 pm</span></a></li>
              <li><a class="act" href="#" data-link><span class="act__name">Private hire</span><span class="act__note">Up to 240 guests</span></a></li>
              <li><a class="act" href="#" data-link><span class="act__name">Visit</span><span class="act__note">Box office and directions</span></a></li>
            </ul>
            <div class="programme__foot" data-extra>
              <a class="tickets" href="#">Book tickets</a>
            </div>
          </nav>

          <canvas class="cloth" data-cloth aria-hidden="true"></canvas>
          <div class="velvet" data-velvet aria-hidden="true"><span></span><span></span></div>
          <div class="apron" data-apron aria-hidden="true"></div>
        </div>
      </div>
    </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.

30 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 sipario» da Animorio e adattala al mio sito

Cosa fa

Su un fondo nero, in alto, un pulsante Menu che ha per icona un piccolo sipario; sotto, un palco in un arco dorato, chiuso da un sipario di velluto rosso con le pieghe profonde, la frangia d’oro sull’orlo che poggia sulle assi e in alto una mantovana a festoni con le rosette dorate. Il velluto respira appena, come in una corrente d’aria, e dalla fessura in mezzo filtra la luce del palco. Col puntatore vicino il bordo delle tende si scosta, si gonfia e si increspa, e lascia intravedere il palco. Premendo Menu (o il sipario stesso) le due tende scorrono verso i lati sul loro binario, una corda le raccoglie a metà altezza e le lega, e le falde di velluto sotto il nodo ondeggiano finché si fermano; intanto si accende l’occhio di bue e le cinque voci (What’s on, Films, Supper club, Private hire, Visit, ognuna con la sua riga) salgono sul palco una dopo l’altra, con il pulsante dorato Book tickets. La voce indicata diventa d’oro. Premendo Close, o scegliendo una voce, o con Esc, i nodi si sciolgono, il velluto ricade e le tende tornano a chiudersi al centro.

Come funziona

La stoffa: punti e fili

Ogni tenda è una griglia di 40 per 34 punti. A ogni passo (fisso, un sessantesimo di secondo) un punto si muove di quanto si è mosso nel passo prima, frenato appena, più la gravità: è l’integrazione di Verlet, che non tiene velocità ma la posizione di prima. Poi i fili fra punti vicini (in orizzontale, in verticale, in diagonale, e a due passi in verticale per non piegarsi troppo) vengono riportati alla loro lunghezza STIFF volte di fila. Un filo resiste tanto all’allungarsi e quasi niente all’accorciarsi: così raccolto il velluto si piega morbido invece di accartocciarsi.

Il binario e le pieghe

La prima riga è appesa ai ganci del binario, che vanno avanti e indietro in profondità (0, 1, 0, −1, con piccole differenze fra una piega e l’altra): i fili orizzontali nascono più lunghi del passo fra due ganci di FULLNESS volte, ed è quella stoffa in più che fa le pieghe. Aprendo, i ganci si avvicinano fino a GATHER della larghezza del palco e le pieghe si fanno più profonde da sole, perché il filo resta lungo uguale.

La corda che lega

Mentre il sipario si apre, una riga a metà altezza viene tirata un po’ a ogni iterazione verso un punto che parte dai ganci e arriva al nodo, in TIE (di lato e in basso): tirata subito al nodo, la stoffa sotto ci girerebbe sopra di colpo. L’apertura stessa (da 0 a 1) avanza dentro la simulazione, un passo alla volta: su un telefono lento che fa meno passi del dovuto la corda non va avanti senza la stoffa.

Il velluto in WebGL

Le tende sono due reti di triangoli con le normali ricalcolate a ogni fotogramma dai punti vicini. Lo shader dà al velluto il suo aspetto: luce diffusa, una lucentezza dove la piega gira di taglio (1 − n.z), buio in fondo alle pieghe secondo la profondità del punto, e un filo di rumore. In basso la frangia: fili d’oro ritorti disegnati con un seno sulla coordinata della stoffa, con le punte sfrangiate scartando dei pixel; sopra, un gallone. La mantovana è lo stesso shader su una forma ferma a festoni, con le rosette disegnate nello shader.

Il puntatore e la corrente

I punti a meno di un sesto del palco dal puntatore vengono spinti di lato (soprattutto lungo il bordo libero, che così si apre), trascinati dal suo movimento e gonfiati in avanti, con un’increspatura che continua anche col puntatore fermo. A riposo una corrente leggera (BREEZE) muove il velluto in basso. La tela lascia passare il puntatore: sotto c’è il menu vero, con i suoi link.

Valori da cambiare

NomeFileDi partenzaCosa cambia
OPENscript.js1.8I secondi del sipario che si apre (chiudendo ci mette un po’ meno).
GATHERscript.js0.3Dove si ferma la cima di ogni tenda aperta, in frazione della larghezza del palco dal suo lato.
TIEscript.js[0.12, 0.52]Dove viene legata ogni tenda: di lato e in basso, in frazioni del palco.
FULLNESSscript.js1.8Quanto il velluto è più largo della metà di palco che copre: di più, pieghe più profonde.
SWAYscript.js1Quanto il sipario si scosta e ondeggia vicino al puntatore.
BREEZEscript.js0.6La corrente d’aria che muove il velluto a riposo.
STIFFscript.js10Quante volte a ogni passo i fili vengono riportati alla loro lunghezza: meno, una stoffa più elastica (e più leggera da calcolare).

Provali nel Playground

Quando usarla

Per il sito di un teatro, un cinema, un club, un ristorante con la sala o un evento: dove aprire il menu può essere un piccolo spettacolo e il velluto c’entra davvero. Per un sito di tutti i giorni è troppo: il sipario chiede un momento di attenzione.

Sul telefono

Il palco si stringe alla larghezza dello schermo e diventa alto; lì le tende si raccolgono più strette (il 70% di GATHER e di TIE), con meno stoffa e il nodo un po’ più in basso, per lasciare libero il menu. Il tocco apre e chiude; il dito che scorre sul sipario lo fa ondeggiare come il mouse.

Con il movimento ridotto

Il sipario non scorre: si dissolve e ricompare già aperto e legato (o già chiuso), con le pieghe ferme, senza corrente e senza ondeggiare sotto il puntatore; le voci compaiono in dissolvenza senza salire.

Trappole

  • Con i fili rigidi anche a comprimersi, il velluto raccolto dalla corda si accartocciava in spigoli e sotto il nodo tornava largo come prima, coprendo il menu: un filo deve resistere all’allungamento e quasi niente all’accorciamento (SOFT, 0,07).
  • Tirare la riga legata subito verso il nodo faceva girare la stoffa sotto sopra la corda, con la frangia rovesciata in alto: il punto verso cui tira parte dai ganci e arriva al nodo col procedere dell’apertura.
  • L’apertura animata con un tween di GSAP, a tempo di orologio, su un telefono lento correva avanti alla stoffa: la simulazione restava indietro e la corda strappava. L’apertura ora avanza dentro il passo della simulazione.
  • I punti in Float64Array per la precisione vanno copiati in un Float32Array prima di passarli a WebGL: letti come float a 32 bit i loro byte diventano una geometria impazzita.
  • Un pavimento con transform: perspective sotto una tela WebGL obbligava il navigatore a ricomporre tutta la scena a ogni fotogramma, da 60 a 10 fotogrammi al secondo senza GPU: le assi ora sono un trapezio piatto con le righe in SVG.
  • In WebGL 1 una uniform usata da entrambi gli shader deve avere la stessa precisione: con highp nel vertex e mediump nel fragment il programma non si collega, e il sipario sarebbe rimasto quello di riserva in CSS.

Librerie

GSAP 3.15.0, Bodoni Moda (font variabile) 5.3.0, Hanken Grotesk (font variabile) 5.3.0.

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

Collaudo

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