Vai al contenuto
animorio
EN
Tutti i componenti

Il menu della metropolitana

Navigazione, SVG, Fisica, TransizioniGSAPImpegnativaPeso compresso: Demo 12 KBGSAP 28 KBGratisScarica lo zip

Un menu come una linea della metropolitana: le voci sono le fermate, e scegliendone una un vagoncino ci va seguendo il tracciato, rallentando nelle curve e ondeggiando, mentre la linea si colora dietro di lui; da una fermata di scambio una diramazione porta alle sottovoci.

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 metro map menu</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/overpass@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/doto@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <div class="layout" data-layout>
      <!-- the menu: a metro line. The stops are links laid over the drawing; a little car rides the line to the stop
           chosen, and the line takes its colour behind it. Venues is an interchange: a branch leads to its halls -->
      <nav class="map" aria-label="Main" data-map>
        <svg class="map__art" viewBox="0 0 360 540" aria-hidden="true" data-art>
          <defs>
            <radialGradient id="beam" cx="0" cy="0.5" r="1" fx="0" fy="0.5">
              <stop offset="0" stop-color="#fff3c4" stop-opacity="0.95" />
              <stop offset="1" stop-color="#fff3c4" stop-opacity="0" />
            </radialGradient>
          </defs>
          <g data-tracks></g>
          <g data-ghost></g>
          <g data-paint></g>
          <path class="route" data-route />
          <g data-marks></g>
          <!-- the car, seen from above: the end with the white lamps is the one it travels towards -->
          <!-- the shadow: three soft layers instead of a blur filter, which would be redrawn at every frame -->
          <g class="car__shadow" data-shadow>
            <rect x="-23" y="-10.5" width="46" height="21" rx="10" />
            <rect x="-21.5" y="-9" width="43" height="18" rx="8.5" />
            <rect x="-20" y="-7.5" width="40" height="15" rx="7" />
          </g>
          <g class="car" data-car>
            <g data-body>
              <ellipse class="car__beam" cx="34" cy="0" rx="16" ry="8" fill="url(#beam)" data-beam />
              <rect class="car__body" x="-20" y="-7.5" width="40" height="15" rx="7" />
              <rect class="car__roof" x="-12.5" y="-4.6" width="25" height="9.2" rx="2.4" />
              <rect class="car__stripe" x="-12.5" y="-1.5" width="25" height="3" rx="1.5" />
              <path class="car__glass" d="M14 -5.4h1.6a3.6 3.6 0 0 1 3.4 3.4v4a3.6 3.6 0 0 1-3.4 3.4H14z" />
              <path class="car__glass" d="M-14 -5.4h-1.6a3.6 3.6 0 0 0-3.4 3.4v4a3.6 3.6 0 0 0 3.4 3.4H-14z" />
              <circle class="car__lamp car__lamp--a" cx="18.6" cy="-5" r="1.25" />
              <circle class="car__lamp car__lamp--a" cx="18.6" cy="5" r="1.25" />
              <circle class="car__lamp car__lamp--b" cx="-18.6" cy="-5" r="1.25" />
              <circle class="car__lamp car__lamp--b" cx="-18.6" cy="5" r="1.25" />
            </g>
          </g>
        </svg>

        <ol class="map__stops">
          <li><a class="stop" href="#" data-stop="welcome" aria-current="page">Welcome</a></li>
          <li><a class="stop" href="#" data-stop="programme">Programme</a></li>
          <li><a class="stop" href="#" data-stop="tours">Tours</a></li>
          <li>
            <a class="stop stop--change" href="#" data-stop="venues">Venues</a>
            <ol class="map__branch" aria-label="Venues">
              <li><a class="stop" href="#" data-stop="docks">Old Docks</a></li>
              <li><a class="stop" href="#" data-stop="glasshouse" data-autoclic>Glasshouse</a></li>
              <li><a class="stop" href="#" data-stop="depot">Tram Depot</a></li>
            </ol>
          </li>
          <li><a class="stop" href="#" data-stop="tickets" data-autoclic>Tickets</a></li>
        </ol>

      </nav>

      <!-- the station board: the stop the car is at, or the one it is going to while it travels -->
      <section class="board" aria-labelledby="board-title" data-board>
        <p class="board__status">
          <span class="board__state" data-status>Now at</span>
          <span class="board__line" data-line>Canal line</span>
        </p>
        <h1 class="board__title" id="board-title"><span class="sr" data-title-name>Welcome</span><span aria-hidden="true" data-title>Welcome</span></h1>
        <p class="sr" aria-live="polite" data-live></p>
      </section>
    </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.

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

Cosa fa

Su un fondo bianco una linea della metropolitana, blu, con le fermate Welcome, Programme, Tours, Venues e Tickets; da Venues, fermata di scambio, parte la diramazione arancio con Old Docks, Glasshouse e Tram Depot. Accanto, un tabellone di stazione scuro dice Now at in verde, il nome della linea e il nome della fermata a puntini luminosi. All’arrivo la linea si disegna dall’alto e le fermate spuntano man mano che il disegno le raggiunge. Passando su una fermata, una linea tratteggiata che marcia mostra la strada che il vagoncino farà. Scegliendola, il vagoncino parte, prende velocità, rallenta prima delle curve e ci si piega dentro ondeggiando, con l’ombra che si sposta verso l’esterno, e dietro di lui la linea si colora; le fermate che passa fanno un piccolo sobbalzo. Il tabellone dice Next stop in ambra e le lettere del nome scorrono fra le lettere dell’alfabeto finché si fermano. Arrivato, frena, la fermata fa un piccolo rimbalzo e il tabellone torna a Now at. Per andare dalla linea sotto lo scambio alla diramazione, il vagoncino si ferma alla fermata di scambio, scambia i fanali e riparte all’indietro sulla diramazione, come i treni veri.

Come funziona

Il tracciato fatto a pezzi

Ogni linea è una fila di punti; gli angoli si arrotondano con un raggio. Lo script ne fa pezzi dritti e archi, ognuno con la sua lunghezza e la sua curvatura (zero o 1/raggio), e scrive lo stesso tracciato come path SVG con i comandi L e A. Così per ogni distanza lungo la linea sa il punto, la direzione e quanto curva, senza getPointAtLength.

Il viaggio e il limite di velocità

Un viaggio è fatto di tratti: lungo una linea, o fino allo scambio e poi sull’altra. Prima di partire lo script calcola un limite di velocità per ogni unità di strada: SPEED sul dritto, √(GRIP / curvatura) in curva, zero dove il vagoncino deve cambiare verso e alla fine; poi, camminando all’indietro, il limite può crescere solo quanto permette la frenata (v² = v₀² + 2·BRAKE·d). A ogni fotogramma il vagoncino accelera di PICKUP verso il limite, con il passo limitato a 1/30 di secondo.

L’ondeggiare

La spinta di lato è v² per la curvatura: muove una molla sull’angolo della cassa, integrata a passi di 1/240 di secondo, che resta indietro entrando in curva e va un po’ oltre uscendone. L’ombra è tre rettangoli trasparenti uno sull’altro (niente filtro di sfocatura, che si ricalcolerebbe a ogni fotogramma) e si sposta verso l’esterno della curva.

La linea che si colora

Ogni viaggio stende la sua vernice: un path per tratto, con pathLength uguale alla sua lunghezza e uno stroke-dasharray che si scopre con stroke-dashoffset man mano che il vagoncino avanza. Lo spazio vuoto del tratteggio è più lungo della linea, così una linea non ancora scoperta non mostra nemmeno il pallino del bordo tondo. Alla partenza seguente la vernice vecchia sfuma.

Fermate, tabellone e anteprima

Le fermate sono link sopra il disegno, messi in unità della piantina con le unità del contenitore (100cqi / 360), così crescono con lei. Il percorso in anteprima è un path tratteggiato sottile (stroke-dasharray 5 4) che marcia con un’animazione CSS. Lo stato del tabellone è solo una parola col suo colore: Now at in verde, Next stop in ambra. Le lettere del tabellone, in Doto, scorrono a caso e si fermano da sinistra a destra con un tween di GSAP.

Valori da cambiare

NomeFileDi partenzaCosa cambia
SPEEDscript.js360La velocità massima del vagoncino, in unità della piantina al secondo (la piantina è larga 360).
PICKUPscript.js760Quanto in fretta prende velocità.
BRAKEscript.js640Quanto forte frena prima di una fermata.
GRIPscript.js1300Quanta spinta di lato regge in curva: più basso, più rallenta.
TURNBACKscript.js0.3I secondi che aspetta dove deve cambiare verso.
SWAYscript.js1Quanto ondeggia la cassa e quanto si sposta l’ombra nelle curve.
DRAWscript.js1.6I secondi in cui la linea si disegna all’arrivo.

Provali nel Playground

Quando usarla

Per il sito di un festival, di una città, di un museo con più sedi, di una conferenza con più sale, o per l’indice di una guida: dove le pagine sono davvero tappe di un percorso e un gruppo di sottopagine può stare su una diramazione. Da cinque a dieci fermate.

Sul telefono

Il tabellone va sopra e la piantina sotto, larga quanto lo schermo fino a 420 pixel; le fermate crescono e si stringono con lei e restano grandi abbastanza per un dito. Senza puntatore non c’è il percorso in anteprima: il tocco fa partire subito il vagoncino.

Con il movimento ridotto

Il vagoncino non viaggia: sparisce in dissolvenza e ricompare alla fermata scelta, mentre la strada fatta compare colorata in dissolvenza. La linea non si disegna all’arrivo, le lettere del tabellone non scorrono, il tratteggio del percorso non marcia e le fermate non sobbalzano.

Trappole

  • Con getPointAtLength e un tween sulla lunghezza il vagoncino andava a velocità costante anche nelle curve strette; costruendo il tracciato a pezzi la curvatura si conosce esattamente e da lì vengono il limite in curva e l’ondeggiare.
  • Un limite di velocità calcolato solo in avanti fa frenare tardi: si sistema camminando all’indietro dalla fine, perché ogni punto deve poter fermarsi in tempo per tutti quelli dopo.
  • Il vagoncino vicino alla fermata andava sempre più piano senza arrivare mai: sotto una velocità minima (9 unità al secondo) finisce la corsa.
  • Al cambio di verso la cassa si girava di colpo a metà attesa: il vagoncino si sposta di un soffio sul tratto nuovo appena si ferma, così aspetta già voltato e sono solo i fanali a cambiare (bianchi davanti, rossi dietro).
  • Un filtro feGaussianBlur sull’ombra, mossa a ogni fotogramma, faceva ricalcolare la sfocatura di continuo e rallentava molto i browser senza scheda video: tre rettangoli semitrasparenti fanno un’ombra morbida senza costi.
  • Con il trattino e lo spazio lunghi uguali, una linea con i capi tondi non ancora scoperta lascia un pallino all’inizio: lo spazio deve essere più lungo della linea.

Librerie

GSAP 3.15.0, Overpass (font variabile) 5.3.0, Doto (font variabile) 5.3.0.

Compressi con gzip, i tre file della demo pesano 12 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