Vai al contenuto
animorio
EN
Tutti i componenti

Il menu di condivisione a moduli

Menu, Transizioni, Hover, TestoCanvas, GSAPImpegnativaPeso compresso: Demo 22 KBGSAP 28 KBGratisScarica lo zip

Il pulsante Share di una playlist: il suo pannello esce dal pulsante quadretto per quadretto, come da una fontana, e ha gli stessi quadretti di un codice QR. Scegliendo QR code il pannello gira i suoi quadretti nel codice vero del link, nell'ordine in cui lo legge un telefono, mentre le lettere del link si accendono; l'evidenziatore è fatto di colonne che si piegano a gradini, e Copy manda una testina gialla lungo il link.

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 share menu made of modules</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/recursive@5.3.0/mono.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the share button of a playlist and the room above it for its panel. The panel is drawn square by square in
         the canvas: it comes out of the button, and its squares are the same size as the squares of the QR code it
         can turn into -->
    <div class="share" data-share>
      <div class="share__shade" aria-hidden="true" data-shade></div>
      <canvas class="share__canvas" aria-hidden="true" data-canvas></canvas>

      <div class="sheet" data-sheet inert>
        <!-- the first page: what is shared, where it can go, and its link -->
        <section class="sheet__page" data-page="menu">
          <div class="sheet__head" data-row>
            <svg class="sheet__cover" viewBox="0 0 44 44" aria-hidden="true">
              <rect width="44" height="44" fill="#123f49"/>
              <path d="M17.5 27a10 10 0 0 1 20 0z" fill="#ffd36e"/><path d="M14 31.5h27" stroke="#ffd36e" stroke-width="1.4" stroke-linecap="round" opacity=".55"/>
              <path d="M0 26.5q5.5-3 11 0t11 0 11 0 11 0V44H0z" fill="#1d6772"/>
              <path d="M0 31.5q5.5-3 11 0t11 0 11 0 11 0V44H0z" fill="#3b9693"/>
              <path d="M0 36.5q5.5-3 11 0t11 0 11 0 11 0V44H0z" fill="#a7d9c8"/>
            </svg>
            <span class="sheet__what">
              <span class="sheet__title" data-title>Slow Tide</span>
              <span class="sheet__meta">Playlist · 24 songs</span>
            </span>
          </div>
          <div class="sheet__rule" aria-hidden="true" data-row></div>

          <div class="menu" id="share-menu" role="menu" aria-label="Share Slow Tide" tabindex="-1" data-menu>
            <a class="item" role="menuitem" tabindex="-1" href="#" data-item="messages" data-row>
              <span class="item__face">
                <svg class="item__icon" viewBox="0 0 24 24" aria-hidden="true"><path data-bubble d="M12 4.6c4.6 0 8.2 2.9 8.2 6.7S16.6 18 12 18c-1 0-2-.13-2.9-.4L5.2 19.6l1.15-3.35C4.8 15 3.8 13.3 3.8 11.3 3.8 7.5 7.4 4.6 12 4.6z"/></svg>
                <span class="item__label">Messages</span>
                <span class="item__end"></span>
              </span>
            </a>
            <a class="item" role="menuitem" tabindex="-1" href="#" data-item="mail" data-row>
              <span class="item__face">
                <svg class="item__icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="3.6" y="5.6" width="16.8" height="12.8" rx="2.6"/><path data-flap d="M4.8 7.2 L12 12.6 L19.2 7.2"/></svg>
                <span class="item__label">Mail</span>
                <span class="item__end"></span>
              </span>
            </a>
            <div class="item" role="menuitem" tabindex="-1" aria-haspopup="true" data-item="qr" data-row>
              <span class="item__face">
                <svg class="item__icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="4" y="4" width="6" height="6" rx="1.4"/><rect x="14" y="4" width="6" height="6" rx="1.4"/><rect x="4" y="14" width="6" height="6" rx="1.4"/><g class="item__bits" data-bits><rect x="14" y="14" width="2.4" height="2.4" rx=".5"/><rect x="17.6" y="17.6" width="2.4" height="2.4" rx=".5"/><rect x="17.6" y="14" width="2.4" height="2.4" rx=".5"/></g></svg>
                <span class="item__label">QR code</span>
                <span class="item__end"><svg class="item__chev" viewBox="0 0 24 24" aria-hidden="true" data-chev><path d="M9.5 6.5 15 12l-5.5 5.5"/></svg></span>
              </span>
            </div>
            <div class="item" role="menuitem" tabindex="-1" data-item="embed" data-row>
              <span class="item__face">
                <span class="item__glyph"><svg class="item__icon" viewBox="0 0 24 24" aria-hidden="true" data-glyph><path data-left d="M8.4 7.6 4 12l4.4 4.4"/><path data-right d="M15.6 7.6 20 12l-4.4 4.4"/><path data-slash d="M13.4 5.8l-2.8 12.4"/></svg><svg class="item__icon item__done" viewBox="0 0 24 24" aria-hidden="true"><path data-tick d="M5 12.6l4.4 4.4L19 7.4"/></svg></span>
                <span class="item__label">Embed</span>
                <span class="item__end">
                  <span class="act" data-act>
                    <span class="act__words"><span class="act__word" data-word="idle">Copy code</span><span class="act__word" data-word="done">Copied</span></span>
                  </span>
                </span>
              </span>
            </div>
            <div class="sheet__rule" role="separator" data-row></div>
            <div class="item item--link" role="menuitem" tabindex="-1" data-item="copy" data-row>
              <span class="item__face">
                <span class="item__glyph"><svg class="item__icon" viewBox="0 0 24 24" aria-hidden="true" data-glyph><path data-half="a" d="M10.6 13.4a3.6 3.6 0 0 0 5.1 0l3-3a3.6 3.6 0 0 0-5.1-5.1l-1.1 1.1"/><path data-half="b" d="M13.4 10.6a3.6 3.6 0 0 0-5.1 0l-3 3a3.6 3.6 0 0 0 5.1 5.1l1.1-1.1"/></svg><svg class="item__icon item__done" viewBox="0 0 24 24" aria-hidden="true"><path data-tick d="M5 12.6l4.4 4.4L19 7.4"/></svg></span>
                <span class="item__url" data-url></span>
                <span class="item__end">
                  <span class="act" data-act>
                    <span class="act__words"><span class="act__word" data-word="idle">Copy</span><span class="act__word" data-word="done">Copied</span></span>
                  </span>
                </span>
              </span>
            </div>
          </div>
        </section>

        <!-- the second page: the panel turns its own squares into the QR code of the link -->
        <section class="sheet__page sheet__page--qr" aria-label="QR code" data-page="qr" hidden>
          <div class="qr__bar">
            <button class="qr__btn" type="button" aria-label="Back to the share options" data-back>
              <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 6.5 9 12l5.5 5.5"/></svg>
            </button>
            <span class="qr__title">Scan to listen</span>
            <button class="qr__btn" type="button" aria-label="Save the QR code as an image" data-save>
              <svg viewBox="0 0 24 24" aria-hidden="true"><path data-down d="M12 4.5v10M8 10.6l4 4 4-4"/><path d="M5 15.5v2.2A1.8 1.8 0 0 0 6.8 19.5h10.4a1.8 1.8 0 0 0 1.8-1.8v-2.2"/></svg>
            </button>
          </div>
          <div class="qr__code" role="img" data-code></div>
          <p class="qr__link" aria-hidden="true" data-caption></p>
        </section>
      </div>

      <button class="trigger" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="share-menu" data-trigger data-autoclic>
        <span class="trigger__pill" data-pill>
          <svg class="trigger__icon" viewBox="0 0 24 24" aria-hidden="true">
            <path d="M8.6 9.8H7.2a1.9 1.9 0 0 0-1.9 1.9v6.4A1.9 1.9 0 0 0 7.2 20h9.6a1.9 1.9 0 0 0 1.9-1.9v-6.4a1.9 1.9 0 0 0-1.9-1.9h-1.4"/>
            <g data-arrow><path d="M12 14.6V3.8"/><path d="M8.6 7.2 12 3.8l3.4 3.4"/></g>
          </svg>
          <span class="trigger__label" data-label>Share</span>
        </span>
      </button>
    </div>
  </main>

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

32 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 menu di condivisione a moduli» da Animorio e adattala al mio sito

Cosa fa

Su un fondo bianco c'è solo un pulsante scuro a pillola, Share, con l'icona della condivisione. Premendolo, la freccia dell'icona parte verso l'alto e dal pulsante esce il pannello, quadretto per quadretto: centinaia di quadretti da 8 pixel salgono dal pulsante come da una fontana, prima quelli vicini e al centro, poi quelli lontani e ai lati, e si posano uno accanto all'altro finché il pannello è intero. Cresce come una cupola, e dove i quadretti atterrano passa un'onda di luce sottile. Le parole di ogni riga compaiono quando i quadretti sotto di loro sono tutti a posto. Il pannello mostra la copertina e il nome della playlist, Slow Tide, poi le destinazioni, ognuna con la sua icona disegnata: Messages, Mail, QR code ed Embed; in fondo, in un campo, il link ondine.example/s/slow-tide con Copy. Passando sulle voci le segue un evidenziatore chiaro, fatto anche lui di colonne di quadretti: parte prima la colonna sotto il puntatore e le altre la seguono, così per strada si piega come un nastro a gradini; dove passa, le parole diventano scure esattamente lungo i suoi gradini. Ogni icona fa la sua mossa: la nuvoletta si gonfia, la busta alza il lembo, i quadretti del QR si girano, le parentesi di Embed si allargano, le due maglie della catena si stringono. Copy manda lungo il link una testina di lettura gialla, una colonna di quadretti con la sua scia: le lettere che attraversa diventano per un attimo monospaziate e più nere, poi l'icona della catena gira su se stessa e lascia il posto a una spunta che si disegna, e Copy diventa Copied. QR code fa crescere il pannello e ne gira i quadretti come stecche di una persiana: prima il bordo chiaro e i tre angoli, in un'onda dall'alto a sinistra, poi i dati, due colonne alla volta dal basso a destra, nell'ordine in cui li legge un telefono, con la testina gialla davanti; sotto il codice le lettere del link si accendono quando i loro bit vengono scritti e restano monospaziate. È un codice QR vero, che la fotocamera di un telefono legge, e si può salvare come immagine. Embed copia il codice per incorporare la playlist; Messages e Mail aprono le loro app con il link già scritto, mentre il pannello torna nel pulsante e il pulsante dice Opening Mail. Chiudendo, i quadretti volano di nuovo nel pulsante, codice QR compreso.

Come funziona

Un numero solo per l'apertura

GSAP porta un numero, fly.p, da 0 a 1 in POUR secondi, e ogni quadretto del pannello ne legge il suo momento: il ritardo è 0,64 volte la sua riga contata dal pulsante, più 0,3 volte il quadrato della sua distanza dal centro, più un poco di suo, e così il pannello cresce a cupola. Ogni quadretto passa in volo il 30% dell'apertura, su una curva di Bézier quadratica da un punto dietro il pulsante al suo posto, col punto di controllo spostato verso l'alto quanto dice FOUNTAIN; cresce dal 45% della misura, si raddrizza e si gonfia un poco atterrando. Quelli posati si disegnano dentro la forma arrotondata del pannello (clip), quelli appena posati restano chiari per 35 millesimi. Alla chiusura lo stesso numero torna a 0 in RETURN secondi: partono prima la cima e i lati, che accelerano verso il pulsante.

Il codice QR fatto qui

Il codice si calcola nello script dal testo di LINK, senza librerie: modo byte, correzione M, versioni da 1 a 10. I bit (modo, lunghezza, i byte, la fine e il riempimento EC 11) diventano parole, ogni blocco riceve la sua correzione di Reed-Solomon sul campo GF(256) col polinomio 0x11D, i blocchi si intrecciano; poi i motivi fissi (i tre angoli, le righe di temporizzazione, i quadrati di allineamento, il formato e la versione), i dati a zig-zag due colonne alla volta dal basso a destra, e le otto maschere provate con le quattro penalità, tenendo la migliore. Il risultato è stato confrontato quadretto per quadretto con la libreria qrcode e letto dalle foto con jsQR.

Gli stessi quadretti

Il pannello è largo 43 quadretti da 8 pixel e il codice, col suo bordo chiaro di 3 quadretti, comincia su un multiplo di 8: ogni quadretto del codice è un quadretto del pannello. Ognuno si gira come una stecca, con l'altezza moltiplicata per il valore assoluto di cos(πu), in FLIP secondi, dal colore del pannello al suo: prima il bordo e i motivi fissi in un'onda diagonale, poi i dati nell'ordine in cui sono posati, con la testa gialla. Ogni lettera del link sa quali quadretti portano il suo byte (le parole del codice ricondotte indietro attraverso l'intreccio dei blocchi): si accende quando vengono scritti e il suo asse MONO del carattere Recursive va a 1.

L'evidenziatore a colonne

L'evidenziatore sono 41 colonne da 8 pixel, ognuna con la sua altezza e il suo posto. Verso una voce nuova ogni colonna parte dopo LAG secondi moltiplicati per la sua distanza dal puntatore e arriva in GLIDE secondi con back.out; da tastiera parte da sinistra. Ogni voce ha sopra una copia scura di sé tagliata da un clip-path polygon() che segue i gradini delle colonne; le parole della faccia hanno lo stesso colore dell'evidenziatore, così sotto di lui spariscono del tutto e si vede solo la copia scura, che non ha bisogno di uno sfondo: sotto, il canvas può disegnare la testina.

La testina e la conferma

Copiando, una testina gialla corre lungo il link in READ secondi: colonne gialle con una scia che si spegne in modo esponenziale, e le lettere a meno di 46 pixel dalla testina prendono l'asse MONO e più peso con font-variation-settings. Poi l'icona gira e si rimpicciolisce, una spunta si disegna al suo posto (stroke-dashoffset) e Copy rotola in DONE con una sfocatura; dopo HOLD secondi torna tutto. Solo una pressione vera scrive negli appunti (navigator.clipboard, e se manca execCommand): il clic dell'anteprima del catalogo non li tocca mai.

Un menu vero

Il pulsante ha aria-haspopup, aria-expanded e aria-controls; il pannello ha role menu e le voci menuitem. Frecce, Home, End, la prima lettera di una voce, Invio e Spazio, Esc (dalla pagina del QR prima torna indietro) e Tab, che chiude; il fuoco torna al pulsante a chi usa la tastiera. Messages e Mail sono link veri, sms: e mailto: col titolo e il link. La pagina del QR ha Indietro e Salva, e il Tab gira fra i due; Salva crea un PNG nero su bianco col bordo di 4 quadretti, pronto da stampare.

Valori da cambiare

NomeFileDi partenzaCosa cambia
LINKscript.jshttps://ondine.example/s/slow-tideIl link che si copia e che diventa il codice QR (fino a 213 byte: uno più lungo si taglia).
POURscript.js0.62I secondi del pannello che esce dal pulsante, quadretto per quadretto.
FOUNTAINscript.js0.6Quanto i quadretti salgono prima di allargarsi: 0 in linea retta, 1 prima dritti in su.
RETURNscript.js0.42I secondi del pannello che torna nel pulsante.
LAGscript.js0.12Quanto resta indietro la parte lontana dell'evidenziatore, in secondi: 0 lo fa muovere tutto insieme.
GLIDEscript.js0.26I secondi che ogni colonna dell'evidenziatore impiega ad arrivare su una voce nuova.
WEAVEscript.js1.3I secondi del codice QR che si scrive, quadretto per quadretto.
READscript.js0.6I secondi della testina gialla che corre lungo il link quando si copia.
DONEscript.jsCopiedLa parola della conferma.
HOLDscript.js1.6I secondi che la conferma resta prima di tornare a Copy.

Provali nel Playground

Quando usarla

Per il pulsante Condividi di una cosa che la gente si passa: una playlist, un articolo, una ricetta, un evento, un prodotto. Il codice QR serve davvero quando chi guarda è al computer e la cosa gli serve sul telefono: la ricetta in cucina, il biglietto alla porta, la playlist in macchina. L'apertura è fatta per farsi notare; in un'app usata tutto il giorno conviene accorciare POUR e RETURN. Il codice si legge se --ink e --paper restano ben diversi: un colore scuro e uno chiaro.

Sul telefono

Il pannello è largo 344 pixel e su uno schermo di 390 ci sta con il suo margine; su uno più stretto di 368 tutto si rimpicciolisce insieme. Si apre e si sceglie col tocco: il dito accende la voce prima che il tocco la scelga. Le voci sono alte 44 pixel, il pulsante 60. Il codice QR mostrato su un telefono si legge con la fotocamera di un altro.

Con il movimento ridotto

Niente quadretti in volo: il pannello compare al suo posto in dissolvenza e se ne va allo stesso modo. L'evidenziatore salta da una voce all'altra senza colonne in ritardo e le icone stanno ferme. Il codice QR compare tutto insieme in dissolvenza; copiando non c'è la testina né l'onda di lettere: Copy diventa Copied in dissolvenza e l'icona lascia il posto alla spunta.

Trappole

  • La luce dei quadretti appena atterrati durava 0,16 secondi: con centinaia di quadretti che atterrano insieme diventava una macchia sgranata in mezzo al pannello. A 35 millesimi è un'onda sottile che lo attraversa.
  • Le parole di una riga comparivano quando era atterrato il centro della riga, sopra i lati ancora in volo e il bianco della pagina: ora aspettano l'ultimo quadretto sotto di loro, quello all'angolo.
  • Al primo fotogramma dell'apertura le parole del pannello si vedevano sul bianco, prima di qualunque quadretto: la loro opacità si scriveva solo al primo aggiornamento del tween. Ora si scrive subito, all'apertura.
  • Copied con la spunta a destra spingeva il link fino a tagliarlo coi puntini: ora la spunta prende il posto dell'icona della voce, e niente cambia larghezza.
  • La testa gialla del codice si spegneva con l'opacità anche sopra i quadretti scuri, e a metà strada diventava marrone: ora resta piena e si spegne solo nell'ultimo tratto.
  • Una copia scura con uno sfondo avrebbe coperto la testina gialla disegnata nel canvas sotto: la faccia ha le parole dello stesso colore dell'evidenziatore, così la copia scura può essere trasparente.
  • Il clic dell'anteprima del catalogo copiava davvero il link e cambiava gli appunti di chi guardava: solo un clic vero (isTrusted) scrive negli appunti.
  • Col movimento ridotto il canvas si dissolveva tutto passando al QR, e per un attimo spariva il pannello; e chiudendo restava disegnato l'evidenziatore. Ora si dissolve solo il codice, e chi nasconde l'evidenziatore chiede di ridisegnare.

Librerie

GSAP 3.15.0, Recursive (font variabile) 5.3.0.

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

Collaudo

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