Vai al contenuto
animorio
EN
Torna alla libreria

La transizione a puzzle

Transizioni, SVGGSAPImpegnativaPeso compresso: Demo 11 KBGSAP 28 KBGratisScarica lo zip

Un cambio di pagina a clic: la pagina si spezza in pezzi di puzzle veri, con le linguette, che si sollevano, ruotano e volano via; i pezzi della pagina nuova arrivano dai lati, si girano e si incastrano con uno scatto, e l’ultimo cade al suo posto con un rimbalzo.

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 jigsaw transition</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- two pages of a made-up studio. Click a link and the page breaks into jigsaw pieces that lift, spin and fly
       away; the pieces of the new page come in from the sides, turn over and click into place, and the last one
       drops in with a bounce -->
  <div class="palco" data-palco>

    <!-- the studio page -->
    <section class="pagina pagina--studio" data-pagina="studio" aria-label="Studio">
      <header class="testa">
        <a class="marchio" href="#studio" data-vai="studio" aria-label="Mortise, studio page">
          <svg class="marchio__segno" viewBox="0 0 32 32" aria-hidden="true">
            <path d="M3 3h11v3.2a3 3 0 1 0 4 0V3h11v11h-3.2a3 3 0 1 0 0 4H29v11H18v-3.2a3 3 0 1 0-4 0V29H3V18h3.2a3 3 0 1 0 0-4H3z"/>
          </svg>
          <span>Mortise</span>
        </a>
        <nav class="voci" aria-label="Main">
          <a href="#studio" data-vai="studio" aria-current="page">Studio</a>
          <a href="#work" data-vai="work">Work</a>
        </nav>
        <span class="testa__posta">hello@mortise.studio</span>
      </header>

      <main class="studio">
        <div class="studio__testo">
          <p class="etichetta" data-onda>Independent design studio</p>
          <h1 class="titolo" tabindex="-1" data-onda>We make the pieces fit.</h1>
          <p class="studio__frase" data-onda>Identities, websites and motion for people who build things carefully. Twelve of us, one long table.</p>
          <a class="tessera" href="#work" data-vai="work" data-autoclic data-onda>
            <svg class="tessera__forma" aria-hidden="true"><path data-forma/></svg>
            <span class="tessera__testo">See the work</span>
            <svg class="tessera__freccia" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg>
          </a>
        </div>
        <div class="composizione" aria-hidden="true" data-onda>
          <span class="forma forma--sole"></span>
          <span class="forma forma--arco"></span>
          <span class="forma forma--righe"></span>
          <span class="forma forma--quarto"></span>
          <span class="forma forma--blocco"></span>
        </div>
      </main>

      <footer class="piede">
        <span>Brand, web, motion</span>
        <span>Booking projects for spring</span>
      </footer>
    </section>

    <!-- the work page -->
    <section class="pagina pagina--work" data-pagina="work" aria-label="Work" inert>
      <header class="testa">
        <a class="marchio" href="#studio" data-vai="studio" aria-label="Mortise, studio page">
          <svg class="marchio__segno" viewBox="0 0 32 32" aria-hidden="true">
            <path d="M3 3h11v3.2a3 3 0 1 0 4 0V3h11v11h-3.2a3 3 0 1 0 0 4H29v11H18v-3.2a3 3 0 1 0-4 0V29H3V18h3.2a3 3 0 1 0 0-4H3z"/>
          </svg>
          <span>Mortise</span>
        </a>
        <nav class="voci" aria-label="Main">
          <a href="#studio" data-vai="studio">Studio</a>
          <a href="#work" data-vai="work" aria-current="page">Work</a>
        </nav>
        <span class="testa__posta">hello@mortise.studio</span>
      </header>

      <main class="lavori">
        <div class="lavori__testa">
          <div>
            <p class="etichetta" data-onda>Selected work</p>
            <h1 class="titolo" tabindex="-1" data-onda>Four recent fits.</h1>
          </div>
          <a class="tessera" href="#studio" data-vai="studio" data-autoclic data-onda>
            <svg class="tessera__forma" aria-hidden="true"><path data-forma/></svg>
            <span class="tessera__testo">Meet the studio</span>
            <svg class="tessera__freccia" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg>
          </a>
        </div>
        <ul class="griglia">
          <li class="lavoro" data-onda>
            <div class="copertina copertina--te" aria-hidden="true"><span></span><span></span></div>
            <p class="lavoro__nome">Halden Tea</p>
            <p class="lavoro__tipo">Identity</p>
          </li>
          <li class="lavoro" data-onda>
            <div class="copertina copertina--bici" aria-hidden="true"><span></span><span></span></div>
            <p class="lavoro__nome">Orbit Bikes</p>
            <p class="lavoro__tipo">Website</p>
          </li>
          <li class="lavoro" data-onda>
            <div class="copertina copertina--libri" aria-hidden="true"><span></span><span></span><span></span></div>
            <p class="lavoro__nome">Paper Moon Press</p>
            <p class="lavoro__tipo">Books</p>
          </li>
          <li class="lavoro" data-onda>
            <div class="copertina copertina--radio" aria-hidden="true"><span></span><span></span></div>
            <p class="lavoro__nome">Lowtide Radio</p>
            <p class="lavoro__tipo">Motion</p>
          </li>
        </ul>
      </main>

      <footer class="piede">
        <span>Brand, web, motion</span>
        <span>Booking projects for spring</span>
      </footer>
    </section>

    <!-- the table under the pages: the pieces fly over it -->
    <div class="tavolo" data-tavolo aria-hidden="true" inert>
      <svg class="tavolo__traccia" data-traccia></svg>
      <div class="tavolo__pezzi" data-pezzi></div>
    </div>
  </div>
  <p class="sr" role="status" aria-live="polite" data-avviso></p>

  <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.

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

Cosa fa

Due pagine di uno studio di design inventato, Mortise: la pagina Studio color carta, col titolo grande e una composizione di forme, e la pagina Work verde bosco, con quattro progetti. All’apertura la prima pagina si compone da sola: i pezzi arrivano dai lati e si incastrano. Al clic su un link (il bottone a forma di pezzo di puzzle, o le voci del menu) sulla pagina si disegnano le linee di taglio di un puzzle, partendo dal link cliccato; i pezzi si sollevano dal tavolo con un’ombra che si allarga, ruotano e volano via verso l’esterno, alcuni girandosi e mostrando il retro di cartone. Sul tavolo scuro resta il tracciato tratteggiato del puzzle nuovo. I pezzi della pagina nuova entrano dai lati capovolti e ruotati, si girano in volo, si fermano un attimo sopra il loro posto e scendono con uno scatto, la linea del taglio che si accende. L’ultimo pezzo, quello in mezzo, cade dall’alto grande e storto, rimbalza nel suo buco e scuote tutto il puzzle; i pezzi intorno fanno un saltello. Poi le linee svaniscono, i pezzi tornano a essere la pagina e le scritte si assestano con una piccola onda.

Come funziona

Ogni pezzo è la pagina intera

Al clic lo script copia la pagina (cloneNode) una volta per pezzo e ritaglia ogni copia con clip-path: path() lungo il contorno del pezzo. La copia è grande quanto lo schermo e spostata all’indietro della posizione del pezzo, così dentro il ritaglio si vede esattamente quel pezzo di pagina, testi e forme compresi. Alle copie si tolgono data-autoclic, data-vai e tabindex, e il tavolo che le contiene è inert e aria-hidden: non si cliccano e non si leggono due volte.

Il contorno con le linguette

Ogni bordo interno è fatto di dieci punti: un inizio e tre curve cubiche (spalla e collo, testa, collo e spalla). Le misure della linguetta sono in parti del lato corto del pezzo (KNOB), un poco storte a caso (WOBBLE), e flip sceglie da che parte sporge. Ogni bordo si calcola una volta: il pezzo sopra lo percorre all’indietro (basta rovesciare l’elenco dei punti), così i due pezzi combaciano al pixel. Su uno schermo alto colonne e righe si scambiano.

Il taglio e il volo

Ogni pezzo ha un SVG col suo contorno, due tracciati (un’ombra e una luce) con pathLength="1": il taglio si disegna con stroke-dashoffset da 1 a 0, prima vicino al link cliccato e poi lontano. Poi il pezzo si solleva (scale LIFT, un’ombra drop-shadow più larga, back.out) e vola via lungo la direzione che va dal link al suo centro con power2.in, girando fino a SPIN gradi. Il punto di rotazione è il centro della cella, non del riquadro con le linguette.

Il retro e lo scatto

I pezzi nuovi partono fuori dallo schermo con scaleX negativo: un pezzo rovesciato. Un livello di cartone dentro il ritaglio si accende quando scaleX è sotto zero (lo controlla onUpdate della timeline con gsap.getProperty), così mentre il pezzo si gira si vede prima il retro e poi la pagina. Arrivato sette pixel sopra il suo posto, il pezzo scende con un fotogramma chiave power3.in fino a scale 0.975, l’ombra si spegne e torna a 1 con elastic.out: è lo scatto dell’incastro.

L’ultimo pezzo e lo scambio

Il pezzo più vicino al centro arriva per ultimo, dall’alto: parte grande (DROP, cioè vicino a noi) e storto, e la sua scala torna a 1 con bounce.out. Il primo contatto di bounce.out cade a 1/2,75 della durata: lì partono la scossa del tavolo (fotogrammi chiave su x e y) e il saltello dei vicini, prima i più vicini. Alla fine i tagli svaniscono e nello stesso fotogramma si mostra la pagina vera e si svuota il tavolo: i pezzi sono già al pixel, quindi lo scambio non si vede.

Valori da cambiare

NomeFileDi partenzaCosa cambia
COLUMNSscript.js4Quanti pezzi in larghezza su uno schermo largo (su uno schermo alto colonne e righe si scambiano).
ROWSscript.js3Quanti pezzi in altezza.
KNOBscript.js0.1La grandezza delle linguette, in parti del lato corto di un pezzo: 0.1 è il puzzle classico, 0.05 linguette piccole, 0.13 grandi.
WOBBLEscript.js0.035Quanto sono irregolari le linguette, perché non ce ne siano due uguali (0 = tutte perfette).
SPINscript.js70Quanto gira al massimo un pezzo in volo, in gradi.
LIFTscript.js1.08Quanto si ingrandisce un pezzo quando si stacca dal tavolo.
DROPscript.js1.7Da quanto in alto cade l’ultimo pezzo: la sua grandezza all’inizio della caduta.
SPEEDscript.js1La velocità di tutta la transizione: 2 è il doppio, 0.5 la metà.

Provali nel Playground

Quando usarla

Per il cambio di pagina di un sito che gioca: uno studio creativo, un portfolio, un negozio di giochi e puzzle, una mostra, una campagna che parla di pezzi che si incastrano (squadre, collaborazioni, prodotti modulari). Va bene anche fra le sezioni di una pagina sola o fra i passi di una presentazione. Da usare dove il cambio è un piccolo evento, non in ogni clic di un’app.

Sul telefono

La stessa transizione con le misure adattate: lo schermo alto si taglia in tre colonne e quattro righe, così i pezzi restano quasi quadrati, e la pagina dei lavori mette i progetti due per riga. Un tocco sul link basta.

Con il movimento ridotto

Niente pezzi e niente movimento: la pagina vecchia svanisce e la nuova compare in dissolvenza. All’apertura la prima pagina compare allo stesso modo.

Trappole

  • Due pezzi vicini disegnati ognuno per conto suo lasciavano fessure e sovrapposizioni lungo il taglio: ogni bordo si calcola una volta sola e uno dei due pezzi lo percorre all’indietro.
  • Con le linguette misurate sulla lunghezza del bordo, sul telefono (pezzi stretti e alti) due linguette opposte si toccavano in mezzo al pezzo: si misurano sul lato corto, centrate sul bordo.
  • Le copie della pagina si portavano dietro data-autoclic: il pilota automatico contava i bottoni delle copie e cliccava quelli. Alle copie si tolgono gli attributi dei comandi.
  • La pagina nuova, tolto inert, si vedeva per un fotogramma prima che la timeline la nascondesse al suo primo giro: pagina vecchia, pagina nuova e tavolo si sistemano con gsap.set nello stesso clic, non dentro la timeline.
  • Un’ombra drop-shadow messa sul ritaglio veniva tagliata dal ritaglio stesso: il filtro sta sull’elemento di fuori, il clip-path su quello di dentro.
  • Il saltello dei vicini sull’atterraggio dell’ultimo pezzo usava la scala, che negli ultimi pezzi arrivati stava ancora rimbalzando con elastic.out: le due animazioni si litigavano la stessa proprietà. Il saltello usa y, che a quel punto è già ferma.

Librerie

GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.

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

Collaudo

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