Vai al contenuto
animorio
EN
Torna alla libreria

La transizione a cartina stradale

Transizioni, 3DGSAP, Solo CSSImpegnativaPeso compresso: Demo 14 KBGSAP 28 KBGratisScarica lo zip

La pagina è una cartina stradale stampata: al clic si stacca dal tavolo e si piega davvero in sei riquadri, le due colonne laterali a fisarmonica e poi a metà. Il pacchetto salta, si gira in aria e si riapre riquadro dopo riquadro sulla pagina stampata sul retro. Il clic dopo la ripiega e torna indietro.

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 road map fold</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/standard.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/standard.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage" data-stage>

    <!-- the desk under the page: you only see it while the map is folded up -->
    <div class="desk" aria-hidden="true">
      <svg class="desk__lines" data-desk viewBox="0 0 1200 800" preserveAspectRatio="xMidYMid slice"></svg>
    </div>
    <div class="shadow" data-shadow aria-hidden="true"></div>

    <!-- the sheet that folds: three columns and two rows of panels, hinged to each other.
         The middle column holds the other two; every column holds its top and bottom panel.
         The panels are filled with copies of the two pages when a fold starts (page A on the front, page B on the back). -->
    <div class="rig" data-rig aria-hidden="true">
      <div class="col col--m" data-col="1">
        <div class="cell cell--top" data-cell data-c="1" data-r="0"></div>
        <div class="cell cell--bot" data-cell data-c="1" data-r="1"></div>
        <div class="col col--l" data-col="0">
          <div class="cell cell--top" data-cell data-c="0" data-r="0"></div>
          <div class="cell cell--bot" data-cell data-c="0" data-r="1"></div>
        </div>
        <div class="col col--r" data-col="2">
          <div class="cell cell--top" data-cell data-c="2" data-r="0"></div>
          <div class="cell cell--bot" data-cell data-c="2" data-r="1"></div>
        </div>
      </div>
    </div>

    <!-- the two real pages, the ones you read and click while the sheet lies flat -->
    <div class="live" data-live>

      <!-- page A: the home of a road trip guide -->
      <section class="page page--a" data-page="a" aria-label="Byway, slow road trips">
        <header class="bar">
          <span class="brand">
            <svg class="brand__mark" viewBox="0 0 32 32" aria-hidden="true">
              <rect x="1" y="1" width="30" height="30" rx="9" class="brand__tile"/>
              <path d="M11 30 C11 22 21 21 21 15 C21 9 12 9 13 2" class="brand__road"/>
              <path d="M11 30 C11 22 21 21 21 15 C21 9 12 9 13 2" class="brand__dash"/>
            </svg>
            <span class="brand__name">Byway</span>
          </span>
          <nav class="links" aria-label="Byway">
            <a href="#routes" class="link"><span class="link__plate" aria-hidden="true"></span><span class="link__text">Routes</span></a>
            <a href="#stays" class="link"><span class="link__plate" aria-hidden="true"></span><span class="link__text">Stays</span></a>
            <a href="#journal" class="link"><span class="link__plate" aria-hidden="true"></span><span class="link__text">Journal</span></a>
          </nav>
          <span class="bar__tag">Spring routes</span>
        </header>

        <div class="hero">
          <div class="hero__text">
            <p class="kicker">Slow road trips on the Northern Coast</p>
            <h1 class="title">Take the <em>long way</em> round.</h1>
            <p class="lede">Hand-drawn routes along the coast road, with the bakeries, lay-bys and empty beaches worth stopping for.</p>
            <button class="cta" type="button" data-go data-autoclic>
              <span class="cta__bg" aria-hidden="true"></span>
              <span class="cta__label">Open the Coast Road</span>
              <span class="cta__map" aria-hidden="true"><i></i><i></i><i></i></span>
            </button>
            <dl class="facts">
              <div class="fact"><dt>Coast road</dt><dd>412 km</dd></div>
              <div class="fact"><dt>Stops</dt><dd>7</dd></div>
              <div class="fact"><dt>Days</dt><dd>6</dd></div>
            </dl>
          </div>

          <figure class="mapcard" data-card>
            <svg class="map" viewBox="0 0 600 500" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Map of the Coast Road from Wren Harbour to Saltmere">
              <rect x="-600" y="-400" width="1800" height="1300" class="map__sea"/>
              <g class="map__waves">
                <path d="M40 70 q10 -6 20 0 t20 0"/><path d="M96 128 q10 -6 20 0 t20 0"/><path d="M30 260 q10 -6 20 0 t20 0"/>
                <path d="M60 352 q10 -6 20 0 t20 0"/><path d="M150 40 q10 -6 20 0 t20 0"/><path d="M22 440 q10 -6 20 0 t20 0"/>
              </g>
              <path d="M54 190 C40 172 66 156 86 166 C108 176 104 202 84 206 C70 210 62 200 54 190Z" class="map__land"/>
              <path d="M262 0 C244 46 196 70 204 122 C212 170 158 196 166 248 C174 300 116 326 128 380 C138 424 100 452 108 500 C112 560 90 640 96 900 H1200 V-400 H300 C286 -260 276 -90 262 0Z" class="map__land"/>
              <path d="M300 -400 C286 -260 276 -90 262 0 C244 46 196 70 204 122 C212 170 158 196 166 248 C174 300 116 326 128 380 C138 424 100 452 108 500 C112 560 90 640 96 900" class="map__beach"/>
              <g class="map__hills">
                <ellipse cx="476" cy="150" rx="92" ry="52"/><ellipse cx="482" cy="146" rx="62" ry="34"/><ellipse cx="488" cy="142" rx="32" ry="17"/>
                <ellipse cx="452" cy="378" rx="78" ry="40"/><ellipse cx="458" cy="374" rx="46" ry="22"/>
              </g>
              <path d="M514 236 C530 222 560 226 566 244 C584 248 586 274 568 282 C560 300 528 298 518 284 C498 284 494 252 514 236Z" class="map__wood"/>
              <path d="M346 456 C356 442 384 444 390 458 C402 462 400 482 386 484 C376 496 350 494 346 480 C332 478 334 460 346 456Z" class="map__wood"/>
              <ellipse cx="384" cy="262" rx="30" ry="15" class="map__lake"/>
              <path d="M332 22 C304 52 254 74 262 124 C270 174 214 198 224 250 C234 302 174 328 188 380 C198 420 168 446 186 484" class="map__road" data-road/>
              <path d="M332 22 C304 52 254 74 262 124 C270 174 214 198 224 250 C234 302 174 328 188 380 C198 420 168 446 186 484" class="map__dash" data-road-dash/>
              <g class="map__scale"><path d="M470 470 H550"/><path d="M470 465 V475 M550 465 V475 M510 467 V473"/><text x="510" y="460">20 km</text></g>
              <g class="pin" data-pin data-at="0.04"><g class="pin__body"><path d="M0 0 C-7 -10 -11 -15 -11 -21 A11 11 0 1 1 11 -21 C11 -15 7 -10 0 0Z"/><circle cy="-21" r="4.2"/></g><text x="16" y="-14">Wren Harbour</text></g>
              <g class="pin" data-pin data-at="0.35"><g class="pin__body"><path d="M0 0 C-7 -10 -11 -15 -11 -21 A11 11 0 1 1 11 -21 C11 -15 7 -10 0 0Z"/><circle cy="-21" r="4.2"/></g><text x="16" y="-14">Lantern Cliffs</text></g>
              <g class="pin" data-pin data-at="0.66"><g class="pin__body"><path d="M0 0 C-7 -10 -11 -15 -11 -21 A11 11 0 1 1 11 -21 C11 -15 7 -10 0 0Z"/><circle cy="-21" r="4.2"/></g><text x="16" y="-14">Gull Bay</text></g>
              <g class="pin" data-pin data-at="0.88"><g class="pin__body"><path d="M0 0 C-7 -10 -11 -15 -11 -21 A11 11 0 1 1 11 -21 C11 -15 7 -10 0 0Z"/><circle cy="-21" r="4.2"/></g><text x="16" y="-14">Saltmere</text></g>
              <g class="car" data-car>
                <g data-car-in>
                  <rect x="-12" y="-6.5" width="24" height="13" rx="4.5" class="car__body"/>
                  <rect x="2" y="-5" width="5.5" height="10" rx="1.6" class="car__glass"/>
                  <rect x="-9" y="-5" width="4" height="10" rx="1.4" class="car__glass"/>
                </g>
              </g>
            </svg>
          </figure>
        </div>
        <div class="creases" aria-hidden="true"><i class="crease crease--v1"></i><i class="crease crease--v2"></i><i class="crease crease--h"></i></div>
      </section>

      <!-- page B: the guide to one route, printed on the back of the same sheet -->
      <section class="page page--b is-off" data-page="b" aria-label="The Coast Road">
        <header class="bar">
          <span class="brand">
            <svg class="brand__mark" viewBox="0 0 32 32" aria-hidden="true">
              <rect x="1" y="1" width="30" height="30" rx="9" class="brand__tile"/>
              <path d="M11 30 C11 22 21 21 21 15 C21 9 12 9 13 2" class="brand__road"/>
              <path d="M11 30 C11 22 21 21 21 15 C21 9 12 9 13 2" class="brand__dash"/>
            </svg>
            <span class="brand__name">Byway</span>
          </span>
          <button class="back" type="button" data-back data-autoclic>
            <span class="back__bg" aria-hidden="true"></span>
            <svg class="back__arrow" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 12 H5 M11 5 L4 12 L11 19"/></svg>
            <span class="back__label">All routes</span>
          </button>
        </header>

        <div class="route">
          <div class="route__head">
            <p class="kicker">Route guide, spring edition</p>
            <h2 class="title">The Coast Road</h2>
            <p class="lede">Six days from Wren Harbour to Saltmere, never more than a mile from the sea.</p>
            <figure class="profile">
              <svg class="profile__svg" viewBox="0 0 600 170" preserveAspectRatio="none" aria-hidden="true">
                <path class="profile__area" data-area/>
                <path class="profile__line" data-line/>
              </svg>
              <figcaption class="profile__cap">
                <span>Wren Harbour</span>
                <span class="profile__peak" data-peak>Highest point: Lantern Cliffs, 212 m</span>
                <span>Saltmere</span>
              </figcaption>
            </figure>
          </div>

          <ol class="legs">
            <li class="leg"><span class="leg__day">Monday</span><span class="leg__way">Wren Harbour<svg class="leg__arrow" viewBox="0 0 40 12" aria-hidden="true"><path d="M1 6 H37 M31 1.5 L37 6 L31 10.5"/></svg>Lantern Cliffs</span><span class="leg__km">64 km</span></li>
            <li class="leg"><span class="leg__day">Tuesday</span><span class="leg__way">Lantern Cliffs<svg class="leg__arrow" viewBox="0 0 40 12" aria-hidden="true"><path d="M1 6 H37 M31 1.5 L37 6 L31 10.5"/></svg>Gull Bay</span><span class="leg__km">58 km</span></li>
            <li class="leg"><span class="leg__day">Wednesday</span><span class="leg__way">Gull Bay<svg class="leg__arrow" viewBox="0 0 40 12" aria-hidden="true"><path d="M1 6 H37 M31 1.5 L37 6 L31 10.5"/></svg>Fennick</span><span class="leg__km">81 km</span></li>
            <li class="leg"><span class="leg__day">Thursday</span><span class="leg__way">Fennick<svg class="leg__arrow" viewBox="0 0 40 12" aria-hidden="true"><path d="M1 6 H37 M31 1.5 L37 6 L31 10.5"/></svg>Old Ferry</span><span class="leg__km">72 km</span></li>
            <li class="leg"><span class="leg__day">Friday</span><span class="leg__way">Old Ferry<svg class="leg__arrow" viewBox="0 0 40 12" aria-hidden="true"><path d="M1 6 H37 M31 1.5 L37 6 L31 10.5"/></svg>Marram Dunes</span><span class="leg__km">69 km</span></li>
            <li class="leg"><span class="leg__day">Saturday</span><span class="leg__way">Marram Dunes<svg class="leg__arrow" viewBox="0 0 40 12" aria-hidden="true"><path d="M1 6 H37 M31 1.5 L37 6 L31 10.5"/></svg>Saltmere</span><span class="leg__km">68 km</span></li>
          </ol>
        </div>
        <div class="creases" aria-hidden="true"><i class="crease crease--v1"></i><i class="crease crease--v2"></i><i class="crease crease--h"></i></div>
      </section>
    </div>

    <p class="sr-only" aria-live="polite" data-say></p>
  </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.

78 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 «La transizione a cartina stradale» da Animorio e adattala al mio sito

Cosa fa

All'apertura una cartina piegata cade sul tavolo scuro, con le curve di livello disegnate, girandosi in aria; atterra con un piccolo schiacciamento e si apre: prima la metà di sotto, poi le due colonne, ognuna con un rimbalzo, e infine si distende e diventa tutta la pagina. È la home di Byway, una guida inventata ai viaggi lenti in auto, con il titolo «Take the long way round.» e una mappa della costa: una macchinina percorre la strada e la disegna dietro di sé, e a ogni tappa cade una puntina con un rimbalzo. Le pieghe restano sulla carta come righe leggere, una valle e una cresta. Al clic su «Open the Coast Road» (o sulla mappa) la pagina cede un poco, le pieghe si fanno più marcate, poi si stacca dal tavolo, si rimpicciolisce e si inclina all'indietro, con l'ombra che si allarga e si sfoca. La colonna di sinistra si chiude sopra quella di mezzo e quella di destra sotto, a fisarmonica, e mentre girano si vede sul loro retro un pezzo dell'altra pagina; poi la striscia si piega a metà. Ogni riquadro prende la sua luce secondo come è girato, e i riquadri piegati stanno uno sopra l'altro con lo spessore della carta. Il pacchetto salta, si gira in aria con un mezzo giro storto e ricade con un piccolo schiacciamento, poi si riapre nell'ordine inverso con i rimbalzi della carta e mostra la pagina stampata sul retro: la guida al percorso «The Coast Road», blu notte, con le sei tappe giorno per giorno e il profilo delle altezze che cresce con un elastico mentre la sua linea si disegna. «All routes» la ripiega e la rigira sulla home. Il pacchetto mostra sempre, su tutte e due le facce, pezzi della pagina che se ne va (anche capovolti, come su una cartina vera): quella nuova compare solo quando si apre.

Come funziona

Sei riquadri incernierati

Il foglio è un contenitore con transform-style: preserve-3d. La colonna di mezzo è la base e tiene dentro le altre due: quella di sinistra ha transform-origin sul bordo destro, quella di destra sul bordo sinistro, così ruotando con rotateY si chiudono proprio lungo la piega. In ogni colonna il riquadro di sotto ha transform-origin sul bordo alto e si piega con rotateX. Ogni riquadro ha due facce con backface-visibility: hidden, il fronte e il retro girato di 180 gradi.

Le due pagine stampate sui riquadri

Le pagine vere stanno sopra il foglio e si usano normalmente. Quando parte la piega se ne fanno copie (cloneNode): ogni faccia tiene la pagina intera, larga e alta come lo schermo, spostata in modo da mostrare solo il suo pezzo. Il fronte mostra la pagina A, il retro la pagina B con le colonne scambiate (la colonna 0 mostra il pezzo 2), perché dopo il giro il foglio è visto da dietro. Nelle copie si tolgono data-autoclic e i ritocchi che arrivano dopo (strada, puntine, profilo).

Lo spessore della carta

Le pieghe arrivano a 180 gradi esatti, ma ogni cerniera gira intorno a una linea staccata dalla carta di mezzo spessore: translateZ, rotazione, translateZ al contrario. Così ogni strato atterra alla sua profondità e i riquadri non si toccano mai (niente sfarfallio). L'ordine degli strati è quello di una cartina vera, e la metà di sotto si piega in avanti o indietro secondo la pagina che se ne va, così le due facce del pacchetto mostrano sempre quella.

Un oggetto solo per tutto il foglio

Scala, inclinazione, giro, torsione, salto e i tre angoli di piega stanno in un oggetto (st) che GSAP anima con una timeline: si alza (power3), si chiude (power2.inOut), salta e si gira, si riapre con back.out e si distende. A ogni aggiornamento una funzione scrive le trasformazioni e calcola la luce: la normale di ogni riquadro, girata come il riquadro, contro una lampada in alto a sinistra, dà l'ombra o il lucido sopra le due facce.

L'ombra sul tavolo

L'ombra è un elemento a parte sul tavolo, fuori dal 3D. La sua misura è l'impronta del foglio, calcolata dai coseni degli angoli (una colonna chiusa non conta più) e stretta dal coseno del giro, così di taglio è una riga. Più il foglio si alza o salta, più l'ombra si sposta, si sfoca e schiarisce. Arrivata la pagina nuova, il foglio sparisce e torna quella vera, identica, che fa partire i suoi ritocchi.

Valori da cambiare

NomeFileDi partenzaCosa cambia
FOLDscript.js0.6I secondi di ogni piega della cartina.
SIZEscript.js0.66Quanto resta grande la cartina piegata in aria (1 = tutto lo schermo); sugli schermi alti un po' di più.
TILTscript.js18Di quanti gradi la cartina piegata si inclina all'indietro.
HOPscript.js1Quanto salta il pacchetto quando si gira (0 = si gira sul posto).
BOUNCEscript.js1Quanto i riquadri vanno oltre e tornano indietro quando si riaprono.
CREASEscript.js0.55Quanto si vedono le pieghe sulla pagina aperta (0 = niente).
SPEEDscript.js1La velocità di tutta la transizione: 2 è il doppio, 0.5 la metà.

Provali nel Playground

Quando usarla

Per passare dalla home a una pagina di dettaglio in un sito di viaggi, di itinerari in auto o in bici, di un ufficio del turismo, di una casa editrice di guide e cartine; per un archivio di mappe, un evento all'aperto con il suo percorso, ogni volta che la pagina nuova è «il retro» di quella di prima.

Sul telefono

La stessa piega con le misure adattate: le due pagine si mettono in colonna e il foglio piegato, che su uno schermo alto è una striscia sottile, resta in aria un po' più grande. Un tocco sul pulsante o sulla mappa la fa partire; gli hover (cartello, mappa che si apre, tappe) ci sono solo col mouse.

Con il movimento ridotto

Niente si piega né si muove: la pagina compare in dissolvenza già completa, con la strada, le puntine e la macchinina al loro posto; al clic la pagina sfuma e compare quella nuova, anche lei già completa.

Trappole

  • Con le pieghe a 180 gradi i riquadri finiscono sullo stesso piano e sfarfallano; con 177 gradi formano un ventaglio che però si incrocia vicino alle cerniere e un riquadro buca l'altro. La soluzione è la cerniera staccata di mezzo spessore (translateZ prima e dopo la rotazione): strati paralleli, a qualche pixel l'uno dall'altro.
  • Se ogni metà di sotto si piega intorno alla sua colonna come viene, l'ordine degli strati non è quello di una carta vera e le facce esterne del pacchetto mostrano tutte e due la pagina nuova. Le profondità vanno scritte strato per strato, e la metà di sotto va in avanti quando parte la pagina A e indietro quando parte la B.
  • Le copie delle pagine portano con sé data-autoclic: il pilota dell'anteprima avrebbe trovato quattordici pulsanti invece di due. Nelle copie l'attributo si toglie, e il foglio ha inert, così niente si raggiunge col tasto Tab.
  • Le copie si cercano anche coi selettori: «.page--a .cta» trova sette pulsanti, e il primo è una copia nascosta nel foglio. Lo script cerca tutto una volta sola, prima di fare le copie, e solo dentro le pagine vere.
  • Un id dentro le pagine (un gradiente, una maschera) sarebbe ripetuto dodici volte nelle copie: le pagine non ne usano, la grana della carta è un SVG in data: nello sfondo.
  • GSAP scrive le trasformazioni di un elemento SVG nel suo attributo transform: la macchinina, che lo script mette sulla strada con setAttribute, ha un gruppo interno per la scala di GSAP, e le puntine uno per la caduta, separato da quello che si gonfia in hover con le transizioni CSS.
  • Una linea che si disegna con dasharray uguale alla lunghezza lascia un puntino alla fine (il cappuccio tondo del tratto successivo): il vuoto va lungo il doppio.

Librerie

GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0, Instrument Sans (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.

Tutte le animazioni