Vai al contenuto
animorio
EN
Torna alla libreria

La maschera che si apre

Transizioni, Scroll, SVGGSAP, Solo CSSMediaPeso compresso: Demo 6,2 KBGSAP + ScrollTrigger 45 KBGratisScarica lo zip

Scorri dentro l’anteprima

Il mare di notte riempie lo schermo, e in mezzo c’è una finestra a forma di maschera da sub: dentro si vede l’acqua, con i pesci e la luce. Scorrendo, tutta la superficie cresce dal centro della maschera fino a venti volte e si sfoca, finché la finestra diventa lo schermo e sei sott’acqua.

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 mask that opens</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/space-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/instrument-serif@5.3.0/400-italic.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <header class="bar">
    <a class="brand" href="#">Lowtide Dive Club</a>
    <nav class="bar__nav" aria-label="Sections">
      <a href="#">Courses</a>
      <a href="#">Sites</a>
      <a href="#">Gear</a>
    </nav>
  </header>

  <!-- one pinned screen with two worlds: under the water at the back, the night surface in front of it -->
  <section class="dive" data-dive>
    <!-- under the water: seen through the mask at the start, the whole screen at the end -->
    <div class="deep" data-deep>
      <svg class="deep__art" viewBox="0 0 1440 900" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
        <defs>
          <linearGradient id="water" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0" stop-color="#5fe0d0"/>
            <stop offset="0.45" stop-color="#1fa6b8"/>
            <stop offset="1" stop-color="#0b4f74"/>
          </linearGradient>
          <linearGradient id="ray" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0" stop-color="#ffffff" stop-opacity="0.55"/>
            <stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
          </linearGradient>
        </defs>
        <rect width="1440" height="900" fill="url(#water)"/>
        <g class="rays" data-rays>
          <path d="M470 0h90l-150 900h-170z" fill="url(#ray)" opacity="0.6"/>
          <path d="M700 0h60l40 900h-190z" fill="url(#ray)" opacity="0.45"/>
          <path d="M930 0h110l230 900h-200z" fill="url(#ray)" opacity="0.5"/>
        </g>
        <!-- fish: each one swims across on its own line -->
        <g data-fish fill="#f6b43c">
          <path d="M0 0c22-16 52-16 72 0-20 16-50 16-72 0zm72 0 22-14v28z"/>
        </g>
        <g data-fish fill="#ff7a59">
          <path d="M0 0c16-11 38-11 52 0-14 11-36 11-52 0zm52 0 16-10v20z"/>
        </g>
        <g data-fish fill="#fff1c9">
          <path d="M0 0c14-9 32-9 44 0-12 9-30 9-44 0zm44 0 13-8v16z"/>
        </g>
        <g data-fish fill="#f6b43c">
          <path d="M0 0c19-13 44-13 61 0-17 13-42 13-61 0zm61 0 19-12v24z"/>
        </g>
        <!-- the sea floor: two hills and a few weeds that sway -->
        <path d="M0 790c180-60 330-40 470 0s320 40 520-10 330-40 450 10v110H0z" fill="#083a57"/>
        <g class="weeds" fill="none" stroke="#0e6b62" stroke-width="10" stroke-linecap="round">
          <path data-weed d="M180 860c-10-60 20-110 0-170"/>
          <path data-weed d="M230 870c10-50-20-90 0-140"/>
          <path data-weed d="M1150 860c-12-70 22-120 0-190"/>
          <path data-weed d="M1210 870c8-50-16-80 0-120"/>
        </g>
      </svg>
      <div class="deep__words">
        <h1 class="deep__title" data-title>Below the surface, <em>everything slows down.</em></h1>
        <a class="deep__link" href="#" data-cta>Book a first dive</a>
      </div>
    </div>

    <!-- the surface at night, with a window in the shape of a diving mask; scrolling makes it grow until you are in -->
    <div class="surface" data-surface>
      <svg class="surface__art" viewBox="0 0 1440 900" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
        <defs>
          <linearGradient id="night" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0" stop-color="#0a1430"/>
            <stop offset="0.55" stop-color="#162a52"/>
            <stop offset="1" stop-color="#0b1834"/>
          </linearGradient>
          <radialGradient id="glow" cx="0.5" cy="0.5" r="0.5">
            <stop offset="0" stop-color="#fff6dc" stop-opacity="0.5"/>
            <stop offset="1" stop-color="#fff6dc" stop-opacity="0"/>
          </radialGradient>
          <!-- the window: everything is drawn except the glass of the mask -->
          <mask id="window" maskUnits="userSpaceOnUse" x="0" y="0" width="1440" height="900">
            <rect width="1440" height="900" fill="#fff"/>
            <use href="#lens" fill="#000"/>
          </mask>
          <path id="lens" data-lens d="M-96-34c0-20 16-26 36-26h120c20 0 36 6 36 26v48c0 26-18 42-44 42-16 0-26-8-34-20-6-10-12-14-18-14s-12 4-18 14c-8 12-18 20-34 20-26 0-44-16-44-42z" transform="translate(720 450) scale(1.15)"/>
        </defs>
        <g mask="url(#window)">
          <rect width="1440" height="900" fill="url(#night)"/>
          <circle cx="1080" cy="190" r="120" fill="url(#glow)"/>
          <circle cx="1080" cy="190" r="34" fill="#fff4d8"/>
          <path d="M0 560h1440v340H0z" fill="#0d2347"/>
          <g class="waves" fill="none" stroke-linecap="round">
            <!-- the script draws the waves: a line that goes up and down every 360 units, long enough to drift -->
            <path data-wave data-y="610" data-amp="16" stroke="#24467a" stroke-width="5"/>
            <path data-wave data-y="690" data-amp="20" stroke="#1d3c6c" stroke-width="7"/>
            <path data-wave data-y="790" data-amp="24" stroke="#173462" stroke-width="9"/>
          </g>
          <!-- the rubber frame of the mask, around the glass -->
          <use href="#lens" fill="none" stroke="#1b2333" stroke-width="22"/>
          <use href="#lens" fill="none" stroke="#3a4a66" stroke-width="3"/>
        </g>
      </svg>
      <div class="surface__words">
        <p class="surface__lead" data-lead>Put the mask on.</p>
        <p class="surface__hint">Scroll to go under.</p>
      </div>
    </div>
  </section>

  <section class="after">
    <p class="after__text">Small groups, slow dives, and a warm drink on the boat on the way back.</p>
  </section>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.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.

22 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 maschera che si apre» da Animorio e adattala al mio sito

Cosa fa

La home di un club di immersioni inventato, Lowtide Dive Club. Si apre sul mare di notte: un cielo blu scuro con la luna e il suo alone, l’orizzonte e tre onde che scorrono lente. In mezzo c’è una maschera da sub con il bordo di gomma scuro, e il suo vetro è una finestra: dentro c’è l’acqua turchese, con raggi di luce che ondeggiano e pesci che passano. Sotto, «Put the mask on.» e un suggerimento. Al primo scorrimento le parole salgono e spariscono nelle loro maschere; poi la superficie cresce dal centro della maschera e si sfoca, il vetro si allarga fino a riempire lo schermo e alla fine la superficie sfuma via. Sott’acqua arrivano il titolo «Below the surface, everything slows down.» e il bottone «Book a first dive», mentre l’acqua si avvicina appena. Tornando su, si riemerge.

Come funziona

Due mondi uno sull’altro

Uno schermo fermo con due strati: dietro l’acqua, davanti la superficie. Sono due SVG con lo stesso viewBox, 1440 per 900, e preserveAspectRatio slice: coprono lo schermo allo stesso modo e il centro del disegno è sempre il centro dello schermo.

La finestra

La superficie è disegnata sotto una mask SVG: un rettangolo bianco, che lascia vedere tutto, e la forma del vetro della maschera in nero, che fa il buco. La stessa forma, ridisegnata con un tratto scuro e spesso, è il bordo di gomma. Il vetro sta al centro del disegno.

Il tuffo

Un ScrollTrigger ferma lo schermo per LENGTH altezze e con lo scrub porta un valore da 0 a 1. La superficie cresce dal centro e si sfoca con filter: blur(BLUR · valore px), la sfocatura dell’originale. La crescita no: l’originale va in linea retta, scala 1 + 19t, ma con una finestra grande come questa lo schermo sarebbe pieno nel primo quarto del tuffo. Qui la scala è (1 + GROW) elevato al valore, cioè cresce dello stesso fattore a ogni passo e la finestra si apre allo stesso ritmo fino in fondo, venti volte alla fine. È un solo strato, che il browser muove sulla scheda grafica. Fra i due punti di FADE la superficie sfuma; l’acqua passa da 1,12 a 1.

Le parole

La frase della superficie è divisa in parole, ognuna dentro uno span che taglia quello che esce: al primo tocco dello scorrimento salgono (yPercent −110, power3.out, 0,04 secondi l’una dall’altra), come le parole SplitText dell’originale. A 0,72 del tuffo arrivano il titolo e il bottone dell’acqua; tornando indietro tutto rientra.

L’acqua che vive

Le onde le disegna lo script: una curva che sale e scende ogni 360 unità, lunga abbastanza da scorrere di 720 e ricominciare. I pesci attraversano ognuno con il suo passo, fra i secondi di SWIM, con una piccola rotazione al posto dell’ondeggiare; i raggi oscillano con skewX e le alghe si piegano intorno al piede.

Valori da cambiare

NomeFileDi partenzaCosa cambia
GROWscript.js19Quanto cresce la superficie oltre la sua misura: alla fine è 1 + GROW volte più grande, venti con 19.
BLURscript.js10Quanto si sfoca la superficie alla fine, in pixel.
LENGTHscript.js2.2Quanto dura il tuffo, in altezze di finestra di scorrimento.
SCRUBscript.js0.6I secondi che il tuffo impiega a raggiungere lo scorrimento.
FADEscript.js[0.82, 1]Fra questi due punti del tuffo la superficie sfuma via.
SWIMscript.js[14, 26]Il pesce più lento e il più veloce, in secondi per attraversare lo schermo.
WAVEscript.js9I secondi che un’onda impiega a scorrere di una lunghezza.

Provali nel Playground

Quando usarla

Per entrare in un mondo: un’immersione, un viaggio, un museo, un gioco, un prodotto in cui si guarda dentro. Serve una forma che si riconosce anche piccola, come una maschera, un oblò, il buco di una serratura o un marchio pieno, e una seconda scena che valga la pena di vedere intera.

Sul telefono

Il disegno copre lo schermo stretto tagliando i lati, quindi la maschera resta grande, circa metà della larghezza. Il dito fa il tuffo come la rotella, con la stessa sfocatura.

Con il movimento ridotto

La superficie non cresce e non si sfoca: scorrendo sfuma nell’acqua fra un quarto e tre quinti del tuffo. Onde, pesci, raggi e alghe stanno fermi, con i pesci in fila nell’acqua.

Trappole

  • La crescita in linea retta dell’originale, 1 + 19t, funziona con un simbolo piccolo come il suo. Con una finestra grande lo schermo è pieno dopo un quarto dello scorrimento e il resto del tuffo non si vede: con 20 elevato a t la finestra si apre sempre allo stesso ritmo.
  • La finestra deve stare al centro esatto del disegno: lo strato cresce dal centro dello schermo, e con preserveAspectRatio slice il centro del disegno è sempre il centro dello schermo. Fuori centro, crescendo, la finestra scappa di lato.
  • Il pesce che ondeggia non deve muovere y: il suo attraversamento la cambia a ogni giro, e due tween sulla stessa proprietà si cancellano a vicenda. L’ondeggiare è una piccola rotazione.
  • La barra in alto non può usare mix-blend-mode: difference: sopra il turchese il bianco diventa arancione. Cambia colore con una classe quando si è sott’acqua.
  • Le onde vanno disegnate più lunghe dello schermo di almeno una lunghezza d’onda per parte, altrimenti mentre scorrono si vede la fine della linea.

Librerie

GSAP 3.15.0, Space Grotesk (font variabile) 5.3.0, Instrument Serif 5.3.0.

Compressi con gzip, i tre file della demo pesano 6,2 KB; le librerie: GSAP + ScrollTrigger 45 KB.

Collaudo

Provata il 2 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.

Tutte le animazioni