
Il banner dei cookie
Consenso, Transizioni, SVG, Fisica
NuovaGratis
I corsi di una scuola di falegnameria in quattro schede, e sotto le schede la fiala di vetro di una livella: la scheda scelta è quella che tiene la bolla fra i suoi due segni. Scegliendone un'altra la fiala si inclina, la bolla corre in salita allungandosi, frena schiacciandosi e trema fra i segni nuovi mentre la fiala torna in piano; il testo nuovo scivola giù per la stessa pendenza e si mette in bolla con lei.
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 spirit level tabs</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the courses of a woodworking school, in four tabs. Under the tabs lies the vial of a spirit level: the chosen
tab is the one whose two marks hold the bubble. Choosing another, the far end of the vial sinks, the bubble
runs uphill to the new tab, stretching as it goes, and the vial settles level again; the new text slides in
down the same slope and levels out with it. The vial and the bubble are drawn by the script in the SVG -->
<div class="level" data-level>
<div class="level__row">
<div class="level__tabs" role="tablist" aria-label="Courses" data-tabs>
<button class="tab" type="button" role="tab" id="tab-joinery" aria-controls="page-joinery" aria-selected="true"><span class="tab__name">Joinery</span></button>
<button class="tab" type="button" role="tab" id="tab-turning" aria-controls="page-turning" aria-selected="false" tabindex="-1"><span class="tab__name">Turning</span></button>
<button class="tab" type="button" role="tab" id="tab-carving" aria-controls="page-carving" aria-selected="false" tabindex="-1"><span class="tab__name">Carving</span></button>
<button class="tab" type="button" role="tab" id="tab-finishing" aria-controls="page-finishing" aria-selected="false" tabindex="-1"><span class="tab__name">Finishing</span></button>
</div>
<div class="vial" aria-hidden="true" data-vial>
<svg class="vial__glass" data-glass>
<defs>
<linearGradient id="vial-liquid" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" style="stop-color: var(--liquid-edge)"/>
<stop offset="0.1" style="stop-color: var(--liquid-deep)"/>
<stop offset="0.26" style="stop-color: var(--liquid-light)"/>
<stop offset="0.4" style="stop-color: var(--liquid)"/>
<stop offset="0.7" style="stop-color: var(--liquid-deep)"/>
<stop offset="0.86" style="stop-color: var(--liquid-light)"/>
<stop offset="0.95" style="stop-color: var(--liquid-deep)"/>
<stop offset="1" style="stop-color: var(--liquid-edge)"/>
</linearGradient>
<linearGradient id="vial-air" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" style="stop-color: var(--air-shade)"/>
<stop offset="0.3" style="stop-color: var(--air)"/>
<stop offset="0.72" style="stop-color: var(--air)"/>
<stop offset="1" style="stop-color: var(--air-glow)"/>
</linearGradient>
<linearGradient id="vial-rim" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" style="stop-color: var(--rim-dark)"/>
<stop offset="0.22" style="stop-color: var(--rim)"/>
<stop offset="0.78" style="stop-color: var(--rim)"/>
<stop offset="1" style="stop-color: var(--rim-dark)"/>
</linearGradient>
<linearGradient id="vial-cap" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" style="stop-color: var(--brass-dark)"/>
<stop offset="0.22" style="stop-color: var(--brass-light)"/>
<stop offset="0.5" style="stop-color: var(--brass)"/>
<stop offset="1" style="stop-color: var(--brass-dark)"/>
</linearGradient>
<radialGradient id="vial-glow" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" style="stop-color: var(--liquid-light); stop-opacity: 1"/>
<stop offset="1" style="stop-color: var(--liquid-light); stop-opacity: 0"/>
</radialGradient>
<clipPath id="vial-inside"><rect data-inside/></clipPath>
</defs>
<rect class="vial__tube" data-tube/>
<rect class="vial__liquid" fill="url(#vial-liquid)" data-liquid/>
<g clip-path="url(#vial-inside)">
<g data-bubble>
<ellipse class="vial__glow" fill="url(#vial-glow)" data-glow/>
<path class="vial__air" fill="url(#vial-air)" stroke="url(#vial-rim)" data-air/>
<path class="vial__shine" data-shine/>
</g>
</g>
<g class="vial__marks" data-marks></g>
<rect class="vial__gleam" data-gleam/>
<rect class="vial__cap" fill="url(#vial-cap)" data-cap="0"/>
<rect class="vial__cap" fill="url(#vial-cap)" data-cap="1"/>
<line class="vial__ring" data-ring="0"/>
<line class="vial__ring" data-ring="1"/>
</svg>
</div>
</div>
<div class="level__pages" data-pages>
<section class="page" role="tabpanel" id="page-joinery" aria-labelledby="tab-joinery" tabindex="0">
<h2 class="page__title">Dovetails by hand</h2>
<p class="page__text">Mark, saw and chop the half-blind dovetails of a small cherry drawer, then glue it up square and take it home.</p>
<dl class="page__facts">
<div><dt>Length</dt><dd>Two days</dd></div>
<div><dt>Group</dt><dd>Six people</dd></div>
<div><dt>Price</dt><dd>€240</dd></div>
</dl>
</section>
<section class="page" role="tabpanel" id="page-turning" aria-labelledby="tab-turning" tabindex="0" hidden>
<h2 class="page__title">A bowl from green wood</h2>
<p class="page__text">Rough out a wet ash log on the lathe, hollow it with a bowl gouge and leave it to dry slowly in a paper bag.</p>
<dl class="page__facts">
<div><dt>Length</dt><dd>One day</dd></div>
<div><dt>Group</dt><dd>Four people</dd></div>
<div><dt>Price</dt><dd>€150</dd></div>
</dl>
</section>
<section class="page" role="tabpanel" id="page-carving" aria-labelledby="tab-carving" tabindex="0" hidden>
<h2 class="page__title">Spoons from a birch log</h2>
<p class="page__text">Split the blank with an axe, shape it on the chopping block and finish the bowl of the spoon with a hook knife.</p>
<dl class="page__facts">
<div><dt>Length</dt><dd>One day</dd></div>
<div><dt>Group</dt><dd>Eight people</dd></div>
<div><dt>Price</dt><dd>€110</dd></div>
</dl>
</section>
<section class="page" role="tabpanel" id="page-finishing" aria-labelledby="tab-finishing" tabindex="0" hidden>
<h2 class="page__title">Oil, wax and shellac</h2>
<p class="page__text">Scrape and burnish a walnut box, then build up a French polish with a cotton rubber until it shines like glass.</p>
<dl class="page__facts">
<div><dt>Length</dt><dd>Half a day</dd></div>
<div><dt>Group</dt><dd>Eight people</dd></div>
<div><dt>Price</dt><dd>€80</dd></div>
</dl>
</section>
</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>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.
60 valori da cambiare
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 «Le schede in bolla» da Animorio e adattala al mio sitoLe schede sono quattro colonne uguali, ognuna col nome in alto e il suo pezzo di fiala sotto, così tutta la colonna si può toccare. La fiala è un SVG sopra le colonne, che non prende i clic: il tubo, il liquido con un gradiente verticale (bordi scuri, la luce raccolta in alto e in basso dal vetro tondo), una riga di riflesso, i tappi d'ottone e una coppia di segni per scheda, misurati dalla larghezza delle colonne e rifatti da un ResizeObserver.
La bolla ha una posizione e una velocità. Una buca sotto la scheda scelta la tira con una molla, ma solo con la forza che la pendenza le concede: il tetto è una piccola spinta di base più una spinta proporzionale al seno dell'inclinazione (tutte e due per RUSH), così la bolla parte quando la fiala si inclina. Il liquido la frena con un attrito proporzionale alla velocità, molto più forte vicino ai segni: è questo che decide quanto va oltre (WOBBLE). Il conto si fa sul ticker di GSAP a passi di 1/240 di secondo, col fotogramma limitato a 1/30.
L'inclinazione è una molla rigida che punta a TILT gradi verso la scheda nuova, un po' di più se è lontana, e torna a zero quando la bolla è a meno di quattro decimi di scheda. La fiala gira con rotation e scende di metà lunghezza per il seno dell'angolo: in pratica gira attorno all'estremo alto, così niente sale verso i nomi. Il mouse aggiunge una seconda inclinazione piccola (LEAN), che sposta la bolla di qualche pixel.
La forma ha una molla sua: vuole essere più lunga quanto più la bolla è veloce (STRETCH), con l'area costante, e quando la bolla frena la molla va oltre e la schiaccia. Il contorno è un'ellisse squadrata di 44 punti, ricalcolata a ogni fotogramma, con la metà dietro che si stringe con la velocità come una goccia; sopra c'è un riflesso e sotto una luce più chiara nel liquido. I segni di una scheda si scuriscono quando la bolla è vicina.
Le righe vecchie escono in 0,13 secondi verso il lato basso; le nuove entrano dal lato alto con power3.out in TEXT secondi, una dopo l'altra. Ogni riga ha una molla che segue l'inclinazione della fiala, più morbida per quelle in basso, e gira attorno al suo bordo di sotto: così il testo arriva storto e si raddrizza con la fiala. Il componente è tenuto dall'alto della pagina e l'altezza del testo cambia con una tween.
È un tablist vero: role tab e tabpanel, aria-selected e una sola scheda raggiungibile col tasto Tab; frecce, Home e End cambiano scheda e portano il fuoco. Quando bolla, fiala, forma e righe sono ferme, il ciclo si toglie dal ticker e non disegna più finché non succede qualcosa. L'anteprima della libreria clicca la scheda dopo quella scelta, che ha data-autoclic.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
START | script.js | 0 | La scheda scelta all'apertura della pagina (0 è la prima). |
TILT | script.js | 1.3 | Quanto si inclina la fiala verso la scheda nuova, in gradi (un po' di più per una lontana). |
RUSH | script.js | 1 | Quanto corre la bolla nel liquido: di più, una bolla più svelta. |
WOBBLE | script.js | 0.5 | Quanto la bolla va oltre e trema quando arriva (0 si ferma morbida, 1 molto vivace). |
STRETCH | script.js | 0.5 | Quanto la bolla si allunga correndo e si schiaccia frenando. |
LEAN | script.js | 0.35 | Quanto si inclina la fiala verso la scheda sotto il mouse, in gradi. |
TEXT | script.js | 0.55 | I secondi in cui entra il testo nuovo. |
Per tre, quattro o cinque schede con nomi brevi, in un sito che ha a che fare con le mani e gli attrezzi: una scuola di falegnameria, un negozio di utensili, uno studio di architettura, un'app per misurare. Anche ferma, la bolla fra i due segni dice subito dove si è.
Il componente è largo al massimo 560 pixel e sul telefono si stringe allo schermo, con 16 pixel di margine per lato: le quattro colonne diventano di 89 pixel, la bolla si accorcia a 34 e la riga delle schede resta alta 60, nome e fiala compresi, tutta da toccare. Il testo nuovo entra da più vicino, perché non esca dallo schermo. Col dito giù su una scheda la fiala si inclina verso di lei come col mouse sopra.
La fiala non si inclina e la bolla non corre: sparisce in dissolvenza fra i segni vecchi e ricompare fra quelli nuovi; il testo nuovo compare in dissolvenza, già dritto. All'apertura tutto è già al suo posto, niente inclinazione col mouse né colpetto.
GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 9,7 KB; le librerie: GSAP 28 KB.
Provata il 5 ottobre 2026
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.
Con l’email e la password del tuo account.
oppure con l’email
Hai fatto l’accesso come .
Con l’account scarichi lo zip di ogni animazione e di ogni pacchetto, gratis.