Vai al contenuto
animorio
EN
Tutti i componenti

Il contatore del cono

Contatori, Fisica, SVGGSAPImpegnativaPeso compresso: Demo 15 KBGSAP 28 KBGratisScarica lo zip

Quante palline di gelato su un cono: col più una pallina cade dall’alto sulla pila, si schiaccia atterrando e rotola al centro di quella sotto, e la torre ondeggia sulle sue molle, più lenta quanto più è alta; col meno quella in cima si solleva e va via. Oltre il massimo la torre vacilla e torna su, sotto il minimo l’ultima pallina non si stacca dal cono.

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 cone stepper</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/opsz.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- how many scoops on a gelato cone: a card with the cone and, under it, a stepper. Every scoop added drops
         onto the stack and squashes, and the tower sways on its springs; pushed past the most scoops it teeters,
         and the last scoop will not come off. The scoops are drawn by the script -->
    <div class="cone-card" data-card>
      <svg class="scene" viewBox="0 0 340 300" aria-hidden="true" data-scene>
        <defs>
          <!-- one scoop, 60 wide, centred on its middle: a dome with a ruffled skirt where the scoop was turned -->
          <path id="gelato-scoop" d="M-30 7C-31.5-13-17.5-29 0-29C17.5-29 31.5-13 30 7C30.8 9.5 32 12.5 30.6 15.6Q26.2 21.5 21.9 17.2Q17.5 23 13.1 16.4Q8.7 22 4.4 17.6Q0 23.4-4.4 16.8Q-8.7 22.2-13.1 17.4Q-17.5 23-21.9 16.6Q-26.2 21.6-30.6 15.6C-32 12.5-30.8 9.5-30 7Z"/>
          <clipPath id="gelato-in-scoop"><use href="#gelato-scoop"/></clipPath>
          <radialGradient id="gelato-round" cx="0.38" cy="0.3" r="0.78">
            <stop offset="0" stop-color="#ffffff" stop-opacity="0.34"/>
            <stop offset="0.34" stop-color="#ffffff" stop-opacity="0"/>
            <stop offset="0.68" stop-color="#3c140a" stop-opacity="0"/>
            <stop offset="1" stop-color="#3c140a" stop-opacity="0.26"/>
          </radialGradient>
          <linearGradient id="gelato-ruffle" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0.7" stop-color="#3c140a" stop-opacity="0"/>
            <stop offset="0.75" stop-color="#3c140a" stop-opacity="0.09"/>
            <stop offset="1" stop-color="#3c140a" stop-opacity="0.2"/>
          </linearGradient>
          <radialGradient id="gelato-contact">
            <stop offset="0" stop-color="#3c140a" stop-opacity="0.34"/>
            <stop offset="1" stop-color="#3c140a" stop-opacity="0"/>
          </radialGradient>
          <linearGradient id="gelato-wafer" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0" class="wafer-light"/>
            <stop offset="0.45" class="wafer-mid"/>
            <stop offset="1" class="wafer-dark"/>
          </linearGradient>
          <radialGradient id="gelato-floor">
            <stop offset="0" class="floor-shade" stop-opacity="0.3"/>
            <stop offset="1" class="floor-shade" stop-opacity="0"/>
          </radialGradient>
          <clipPath id="gelato-in-cone"><path d="M-27.5 3H27.5L3.4 84.5Q0 90-3.4 84.5Z"/></clipPath>
          <!-- the very top of the scene fades out, so a scoop dropped onto a tall stack comes in softly -->
          <linearGradient id="gelato-fade" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0" stop-color="#ffffff" stop-opacity="0"/>
            <stop offset="0.1" stop-color="#ffffff" stop-opacity="1"/>
          </linearGradient>
          <mask id="gelato-top" maskUnits="userSpaceOnUse" x="0" y="0" width="340" height="300">
            <rect width="340" height="300" fill="url(#gelato-fade)"/>
          </mask>
        </defs>

        <g mask="url(#gelato-top)">
          <!-- the world: the cone's rim at 0 0, the floor 100 below; the script scales it to fit the most scoops -->
          <g data-world>
            <ellipse class="floor" cx="0" cy="100" rx="30" ry="5" fill="url(#gelato-floor)" data-floor/>
            <g data-cone>
              <ellipse class="cone__mouth" cx="0" cy="-2.5" rx="28" ry="3.6"/>
              <path class="cone__body" d="M-27.5 3H27.5L3.4 84.5Q0 90-3.4 84.5Z" fill="url(#gelato-wafer)"/>
              <g class="cone__grid" clip-path="url(#gelato-in-cone)" data-grid></g>
              <path class="cone__rim" d="M-30-2.5C-20-4.8 20-4.8 30-2.5L30.4 4.5C20 8.2-20 8.2-30.4 4.5Z"/>
              <path class="cone__rim-edge" d="M-30.4 4.5C-20 8.2 20 8.2 30.4 4.5"/>
              <path class="cone__rim-light" d="M-28-2C-18-4.2 18-4.2 28-2"/>
              <ellipse class="cone__contact" cx="0" cy="3" rx="27" ry="6" fill="url(#gelato-contact)" data-cone-contact/>
            </g>
            <g data-scoops></g>
            <g data-flying></g>
          </g>
        </g>
      </svg>

      <div class="order">
        <div class="order__label">
          <span class="order__name" id="scoops-name">Scoops</span>
          <span class="order__note" id="scoops-note"><span class="order__note-in" data-note>€1.90 each</span></span>
        </div>

        <div class="stepper">
          <button class="stepper__btn" type="button" aria-label="Remove one scoop" data-less>
            <span class="stepper__disc" aria-hidden="true">
              <svg viewBox="0 0 24 24"><path d="M6.5 12h11"/></svg>
            </span>
          </button>
          <input class="stepper__value" type="text" inputmode="numeric" autocomplete="off" spellcheck="false" maxlength="2" role="spinbutton" aria-labelledby="scoops-name" aria-describedby="scoops-note" aria-valuemin="1" aria-valuemax="4" aria-valuenow="3" aria-valuetext="3 scoops" value="3" data-value>
          <button class="stepper__btn" type="button" aria-label="Add one scoop" data-more data-autoclic>
            <span class="stepper__disc" aria-hidden="true">
              <svg viewBox="0 0 24 24"><path d="M6.5 12h11M12 6.5v11"/></svg>
            </span>
          </button>
        </div>
      </div>

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

41 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 contatore del cono» da Animorio e adattala al mio sito

Cosa fa

Su un fondo bianco una scheda verde menta con gli angoli molto tondi. In alto un cono di cialda con la griglia pressata, sospeso sopra la sua ombra, e sopra tre palline: fragola, pistacchio e stracciatella, ognuna con la sua gonna arricciata, i pezzetti dentro, il segno della paletta e l’ombra di quella sopra. Aprendo la pagina le tre palline cadono dall’alto una dopo l’altra e si posano sul cono. In basso, a sinistra, Scoops e sotto €1.90 each; a destra una pillola color cioccolato alta 60 pixel con il meno, il numero 3 e il più. Col più una pallina di cioccolato entra dall’alto della scheda, cade, si schiaccia atterrando, rotola al centro di quella sotto e la torre ondeggia un poco; il cono, tenuto come da una mano, cede di un paio di pixel. Arrivati a 4 il più si spegne e sotto l’etichetta sale Max 4 per cone, in rosso. Premendo ancora il più il numero non cambia: la torre viene spinta via dal più, si piega di lato, le palline scivolano un poco una sull’altra, poi torna su ondeggiando; tenendo premuto resta piegata finché non la si lascia. Col meno la pallina in cima si solleva e svanisce salendo, e la pila, più leggera, fa un piccolo rimbalzo. Sotto l’1 l’ultima pallina viene tirata su di qualche pixel e ricade sul cono schiacciandosi, e sotto l’etichetta compare At least 1 scoop. Clic veloci si sommano: col più cade una colonna di palline che atterrano una sull’altra, e un meno mentre cadono rimanda in su l’ultima. Il numero si può anche scrivere: una cifra scritta prende subito il suo posto.

Come funziona

Una pallina disegnata una volta

La pallina è un solo path in un riquadro largo 60: una cupola e sotto una gonna con sette lobi, la parte arricciata dove la paletta l’ha girata. Ogni pallina lo riusa con <use> e ci mette sopra il suo gusto (una variabile CSS), i pezzetti sparsi sempre allo stesso modo da un generatore con il seme, due sfumature (luce in alto a sinistra, ombra sul bordo e sulla gonna), il segno della paletta e l’ombra di quella sopra, tagliata dalla forma stessa con un clipPath in coordinate locali. Il cono ha la griglia di cialda in due famiglie di righe inclinate, ognuna con il suo filo di luce.

Una catena di giunti

Ogni pallina sta su quella sotto in un giunto: un angolo su una molla, uno spostamento dal centro che rotola indietro a zero e un’altezza che si schiaccia (1 = tonda). A ogni fotogramma la torre si ricostruisce dal cono in su: il punto d’appoggio, il centro (altezza per schiacciamento), il punto che offre alla pallina sopra. Lo schiacciamento è ancorato al punto d’appoggio, così una pallina schiacciata abbassa tutte quelle sopra e la botta corre lungo la pila.

Il peso di sopra

Ogni giunto è tirato di lato dal seno dell’inclinazione di tutte le palline sopra di lui: una torre alta è più morbida e più lenta di una bassa. Il carico accoppia tutti i giunti, e il modo più morbido in cui si piegano insieme è 1 / (4 sin²(π / (4n + 2))) volte più morbido di un giunto solo (8,3 volte con quattro palline): la spinta del peso resta ai due terzi di quella soglia, così anche la torre più alta torna sempre su.

La frusta, senza energia in più

Un giunto che accelera spinge i vicini dall’altra parte, come l’inerzia di una catena: le accelerazioni si trovano risolvendo una matrice delle masse tridiagonale e simmetrica (1 sulla diagonale, WHIP accanto) con l’algoritmo di Thomas. Simmetrica vuol dire che lo scambio fra i giunti non aggiunge mai energia: la cima resta indietro rispetto alla base e la frusta si spegne. Tutto è integrato con Euler semi-implicito in passi di 1/240 di secondo, con il passo del fotogramma limitato a 1/30.

Atterrare senza scatti

Una pallina nuova parte appena sopra la scheda con la velocità THROW e cade con GRAVITY, guidata verso la cima della pila ma mai proprio al centro. Quando il suo centro arriva all’altezza giusta sopra il punto d’appoggio diventa parte della torre tenendo esattamente il posto che aveva: lo spostamento dal centro viene dalla distanza di lato, l’angolo dall’inclinazione della pila, lo schiacciamento dalla distanza in verticale. Poi riceve la botta: lo schiacciamento, una parte alle palline sotto, il cono che cede, e una spinta ai giunti dal lato in cui è atterrata. Con clic veloci ogni pallina parte sopra la precedente ancora in volo, e un meno manda in su prima quella più alta che sta cadendo.

I limiti, le mani e i tasti

Oltre il massimo ogni giunto riceve una velocità verso sinistra, un po’ di più salendo, perché la cima si pieghi di circa TEETER gradi, e le palline scivolano una sull’altra; spinte ripetute aggiungono sempre meno e due fermi morbidi (su ogni giunto e sull’inclinazione di ogni pallina) non la lasciano mai cadere. Sotto il minimo una timeline GSAP tira su l’ultima pallina di 9 pixel e la lascia ricadere. Un pulsante tenuto premuto ripete sempre più in fretta (HOLD); il numero è un campo con role="spinbutton" e aria-valuenow, risponde alle frecce, a Home e a Fine, e prende la cifra scritta dovunque fosse il cursore; una regione aria-live dice il numero a clic finiti e sempre i limiti.

Valori da cambiare

NomeFileDi partenzaCosa cambia
MINscript.js1Il numero più piccolo di palline (0 lascia il cono vuoto).
MAXscript.js4Il numero più grande di palline, fino a 7: oltre 4 il cono si rimpicciolisce per farle stare.
STARTscript.js3Le palline quando si apre la pagina.
THROWscript.js600La velocità con cui una pallina entra dall’alto della scheda, in pixel al secondo.
GRAVITYscript.js2400Quanto accelera una pallina che cade, in pixel al secondo per secondo.
SQUASHscript.js0.2Quanto si schiaccia una pallina atterrando (0 = per niente).
SWAYscript.js[100, 0.3]Le molle della torre: quanto sono dure e quanto sono frenate (1 = niente ondeggiamento).
TEETERscript.js17Di quanti gradi si piega la torre spinta oltre il massimo.
HOLDscript.js[0.42, 0.16, 0.06]Tenendo premuto un pulsante: l’attesa prima che ripeta, il primo intervallo e il più corto, in secondi.
WORDscript.js['scoop', 'scoops']La parola letta dopo il numero, per una e per più.

Provali nel Playground

Quando usarla

Per scegliere quante cose dove la quantità si può far vedere: palline, porzioni, fette, piani di una torta, libri su una pila, pacchi in una spedizione. Sta bene in un ordine da asporto o in un configuratore di prodotto, dove il limite è vero (un cono non regge più di tanto) e conviene che si senta: la torre che vacilla lo dice prima della scritta.

Sul telefono

Su un telefono la scheda sta in 390 pixel con tutto quello che ha, i pulsanti sono dischi da 48 pixel. Il dito fa come il mouse: un tocco conta subito, tenuto premuto ripete sempre più in fretta e oltre il massimo tiene la torre piegata; la pressione lunga non apre menu e due tocchi veloci non ingrandiscono la pagina. Toccando il numero si apre la tastiera numerica.

Con il movimento ridotto

Le palline non cadono, non si schiacciano e la torre non ondeggia: una pallina aggiunta compare al suo posto con una dissolvenza e una tolta svanisce dove sta. Oltre i limiti la torre resta ferma e cambia solo la nota sotto l’etichetta, che sfuma invece di scorrere; i pulsanti non si rimpiccioliscono premuti.

Trappole

  • La torre col peso delle palline sopra cadeva di lato e restava piegata. Il carico accoppia tutti i giunti, e il modo in cui si piegano tutti insieme è molto più morbido di un giunto solo: con quattro palline 8,3 volte. Il peso va tenuto sotto la durezza divisa per quel numero, 1 / (4 sin²(π / (4n + 2))); qui ai due terzi.
  • Per la frusta si toglieva a ogni giunto un po’ dell’accelerazione di quello sotto: una spinta in un verso solo, che con poco freno metteva energia e la torre ondeggiava per secondi. Con la matrice delle masse simmetrica, risolta con l’algoritmo di Thomas, l’energia non cresce mai.
  • Spinte ripetute contro il massimo, tenendo premuto, si sommavano fino a piegare la torre di più di 60 gradi. Ogni spinta ora vale tanto meno quanto più la cima è già piegata, e due fermi morbidi tengono ogni giunto entro 17 gradi e ogni pallina entro 32 dalla verticale.
  • Al primo clic la pallina nuova, che non aveva ancora una posizione, per un fotogramma era disegnata sul bordo del cono, sopra quella in fondo. Ora nasce già al suo posto, sopra la scheda, e la scena si ridisegna subito dopo il clic.
  • Una pallina che compariva a mezz’aria con una dissolvenza sembrava materializzarsi. Ora entra sempre dall’alto della scheda attraverso una maschera che sfuma i primi 30 pixel della scena, e con MAX oltre 4 la scena si rimpicciolisce perché la pila più alta resti sotto quella sfumatura.
  • I gusti dati nell’ordine in cui le palline nascevano cambiavano i colori della torre dopo aver tolto e rimesso: ogni posto della pila ha il suo gusto, così lo stesso numero si vede sempre uguale.
  • La spinta contro il limite usava overwrite: true sul numero e uccideva a metà il suo saltello: il numero restava spostato e sbiadito. Con overwrite: 'auto' ogni tween lascia stare le proprietà degli altri.
  • Playwright, e con lui il collaudo, non clicca un pulsante con aria-disabled: il segno data-autoclic passa all’altro pulsante appena si spinge contro un limite, e all’inizio se si parte già dal massimo.
  • Scrivendo una cifra dopo quella che c’era il campo diventava 32: si legge solo la cifra appena scritta (InputEvent.data), e il campo si seleziona da solo, così la cifra dopo sostituisce. Un incollato o una cancellazione si leggono invece dal campo intero.
  • Col movimento ridotto le due note sotto l’etichetta sfumavano insieme e per un attimo si vedevano una sopra l’altra: ora la vecchia svanisce prima che arrivi la nuova.

Librerie

GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.

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

Collaudo

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

Tutti i componenti