Vai al contenuto
animorio
EN
Tutti i componenti

Il menu del radar

Menu, SVG, Hover, TestoGSAP, Solo CSSMediaPeso compresso: Demo 8,3 KBGSAP 28 KBGratisScarica lo zip

Nello stile minimal: il menu di un piccolo porto come uno schermo radar su fondo nero. Premendo Menu, sotto il pulsante si disegnano gli anelli, la croce e le tacche della ghiera, e il fascio comincia a girare: ogni voce si accende quando il fascio ci passa sopra e poi si spegne piano, come il fosforo. Col puntatore il fascio si ferma sulla voce che si sta guardando; la pagina in cui sei è arancione segnale.

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 radar menu</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the menu of a small harbour, drawn as a radar screen. At rest only the Menu button. Pressed, the range rings
         and the bezel are drawn under it and the beam starts to turn: every link lights up as the beam passes over
         it and fades again slowly, like phosphor. The pointer (or the finger, or the arrow keys) takes the beam and
         holds it on a link; the page you are on is marked in signal orange -->
    <div class="radar" data-frame>
      <button class="toggle" type="button" aria-expanded="false" aria-controls="radar-menu" data-toggle data-autoclic>
        <span class="toggle__icon" aria-hidden="true">
          <svg viewBox="-10 -10 20 20" width="20" height="20">
            <circle class="toggle__ring" r="8.2"></circle>
            <circle class="toggle__ring toggle__ring--in" r="4.2"></circle>
            <line class="toggle__beam" data-icon-beam x1="0" y1="0" x2="0" y2="-8.2"></line>
          </svg>
        </span>
        <span class="toggle__words" aria-hidden="true">
          <span class="toggle__word" data-word-open>Menu</span>
          <span class="toggle__word" data-word-close>Close</span>
        </span>
        <span class="sr" data-toggle-name>Open the menu</span>
      </button>

      <nav class="menu" id="radar-menu" aria-label="Main" data-menu inert>
        <div class="scope" data-scope>
          <svg class="scope__grid" viewBox="-100 -100 200 200" aria-hidden="true" data-grid>
            <g data-rings>
              <circle class="scope__ring" r="24" pathLength="1"></circle>
              <circle class="scope__ring" r="48" pathLength="1"></circle>
              <circle class="scope__ring" r="72" pathLength="1"></circle>
              <circle class="scope__ring scope__ring--edge" r="96" pathLength="1"></circle>
            </g>
            <line class="scope__cross" data-cross x1="0" y1="0" x2="0" y2="-96" pathLength="1"></line>
            <line class="scope__cross" data-cross x1="0" y1="0" x2="96" y2="0" pathLength="1"></line>
            <line class="scope__cross" data-cross x1="0" y1="0" x2="0" y2="96" pathLength="1"></line>
            <line class="scope__cross" data-cross x1="0" y1="0" x2="-96" y2="0" pathLength="1"></line>
            <g data-ticks></g>
            <path class="scope__north" data-north d="M -3 -103 L 0 -98.5 L 3 -103"></path>
          </svg>
          <div class="scope__sweep" aria-hidden="true" data-sweep></div>
          <div class="scope__beam" aria-hidden="true" data-beam></div>

          <ul class="blips" data-blips>
            <li class="blip" data-blip data-angle="36" data-range="0.6"><a class="blip__link" href="#moorings" data-link>
              <span class="blip__mark" aria-hidden="true" data-mark><svg viewBox="-5 -5 10 10"><path d="M -4 0 H 4 M 0 -4 V 4"></path></svg></span>
              <span class="blip__text"><span class="blip__word" data-word>Moorings</span><span class="blip__note" data-note>Berths and fuel</span></span>
            </a></li>
            <li class="blip" data-blip data-angle="122" data-range="0.6"><a class="blip__link" href="#school" aria-current="page" data-link>
              <span class="blip__mark" aria-hidden="true" data-mark><svg viewBox="-5 -5 10 10"><path d="M -4 0 H 4 M 0 -4 V 4"></path></svg></span>
              <span class="blip__text"><span class="blip__word" data-word>School</span><span class="blip__note" data-note>Learn to sail</span></span>
            </a></li>
            <li class="blip" data-blip data-angle="196" data-range="0.5"><a class="blip__link" href="#boatyard" data-link>
              <span class="blip__mark" aria-hidden="true" data-mark><svg viewBox="-5 -5 10 10"><path d="M -4 0 H 4 M 0 -4 V 4"></path></svg></span>
              <span class="blip__text"><span class="blip__word" data-word>Boatyard</span><span class="blip__note" data-note>Repairs, winter</span></span>
            </a></li>
            <li class="blip" data-blip data-angle="264" data-range="0.5"><a class="blip__link" href="#tides" data-link>
              <span class="blip__mark" aria-hidden="true" data-mark><svg viewBox="-5 -5 10 10"><path d="M -4 0 H 4 M 0 -4 V 4"></path></svg></span>
              <span class="blip__text"><span class="blip__word" data-word>Tides</span><span class="blip__note" data-note>Today and weekly</span></span>
            </a></li>
            <li class="blip" data-blip data-angle="330" data-range="0.36"><a class="blip__link" href="#contact" data-link>
              <span class="blip__mark" aria-hidden="true" data-mark><svg viewBox="-5 -5 10 10"><path d="M -4 0 H 4 M 0 -4 V 4"></path></svg></span>
              <span class="blip__text"><span class="blip__word" data-word>Contact</span><span class="blip__note" data-note>Harbour office</span></span>
            </a></li>
          </ul>
        </div>
      </nav>
    </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.

31 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 del radar» da Animorio e adattala al mio sito

Cosa fa

Su un fondo nero c'è solo un pulsante a pillola col contorno sottile: dentro, un piccolo radar disegnato a filo e la parola Menu. Col mouse sopra la lancetta del radarino fa un quarto di giro e la parola si allarga e prende peso. Premuto, Menu scivola via e arriva Close, e sotto si disegna lo schermo: quattro anelli che si chiudono uno dopo l'altro dal centro verso il bordo, la croce che parte dal centro, settantadue tacche che compaiono in giro sulla ghiera e un segno arancione al nord. Poi parte il fascio, una riga sottile con dietro una scia chiara che sfuma: il primo giro è veloce e rallenta fino a un giro ogni 3,6 secondi, e la lancetta del pulsante gira con lui. Le voci, Moorings, School, Boatyard, Tides e Contact, sono crocette con la parola accanto, sparse sullo schermo a distanze diverse dal centro, e non si vedono finché il fascio non le trova. Quando il fascio ci passa sopra la voce si accende tutta: la parola diventa piena e larga, sotto compare una nota (Berths and fuel, Learn to sail…) e la crocetta gira di 45 gradi; poi in due secondi e mezzo si spegne, fino a restare appena visibile. Col puntatore dentro lo schermo il fascio lo segue su una molla, e sopra una voce si ferma lì e la tiene accesa; uscendo riprende a girare. La pagina in cui sei, School, è arancione. Scegliendo una voce la parola si schiaccia un attimo, diventa arancione e lo schermo si richiude al contrario: le voci si spengono, le tacche spariscono in senso antiorario e gli anelli si cancellano verso il centro.

Come funziona

Lo schermo che si disegna

Anelli e croce sono cerchi e righe SVG con pathLength 1 e stroke-dasharray 1 1: la timeline porta stroke-dashoffset da 1 a 0, gli anelli sfalsati di 0,09 secondi dal centro al bordo. Le 72 tacche della ghiera si creano nello script (una ogni 5 gradi, più lunghe ogni 30) e compaiono in senso orario con uno stagger di DRAW·0,7/72. Lo spessore del tratto si calcola dal diametro (200/d unità del viewBox), così resta un pixel a ogni misura.

Il fascio e la scia

Il fascio è un div largo un pixel con transform-origin in basso, la scia un div tondo con un conic-gradient che va da trasparente a 292 gradi a un velo chiaro a 360: tutti e due ruotano dello stesso angolo, scritto a ogni fotogramma in un ciclo requestAnimationFrame col passo limitato a 1/30 di secondo. Il primo giro corre 2,6 volte più veloce e rallenta con power2.out fino a SWEEP secondi a giro.

La luce delle voci

Ogni voce ha una luce da 0 a 1: quando il fascio arriva entro BEAM gradi la luce sale a 1, poi scende di dt/FADE a ogni fotogramma. La luce comanda l'opacità (da REST a 1), il peso e la larghezza di Mona Sans in variabili CSS lette da font-variation-settings, la nota sotto e la rotazione della crocetta. Prima che il fascio la trovi la prima volta una voce è invisibile e non si può cliccare.

Il puntatore prende il fascio

Dentro lo schermo l'angolo del puntatore (atan2 dal centro) diventa la meta del fascio, che ci va su una molla: la velocità cresce di SPRING[0] per la distanza e cala di SPRING[1] per la velocità, sempre per la strada più corta. Sopra una voce, o col fuoco da tastiera su di lei, la meta è l'angolo della voce e la sua luce resta a 1. Uscendo il fascio riparte da fermo e riprende velocità in 0,7 secondi.

Scegliere e richiudere

Il clic schiaccia la parola a 0,95 e torna, sposta aria-current (e l'arancione) e dopo 0,75 secondi lo schermo si richiude al contrario: voci e fascio spengono, le tacche spariscono dall'ultima alla prima, la croce e gli anelli tornano a stroke-dashoffset 1 dal bordo verso il centro.

Tastiera e giro dell'anteprima

Il pulsante ha aria-expanded e aria-controls, la nav è inert da chiusa. Invio o Spazio aprono e portano il fuoco sulla pagina corrente; le frecce passano da una voce all'altra nell'ordine in cui le incontra il fascio, Tab gira fra pulsante e voci, Esc chiude. Il clic dell'anteprima (detail 0, senza tasti appena premuti) fa un giro: apre, lascia girare il fascio, lo ferma su due voci e ne sceglie una.

Valori da cambiare

NomeFileDi partenzaCosa cambia
SWEEPscript.js3.6I secondi che il fascio impiega per un giro intero.
FADEscript.js2.4I secondi che una voce impiega a spegnersi dopo il passaggio del fascio.
BEAMscript.js7Quanti gradi da ogni lato del fascio accendono una voce.
RESTscript.js0.3Quanto si vede una voce fra due passaggi del fascio (da 0 a 1).
WEIGHTscript.js[300, 640]Il peso di una parola al buio e proprio sotto il fascio (da 100 a 900).
WIDTHscript.js[82, 114]La larghezza di una parola al buio e sotto il fascio (da 75 a 125).
SPRINGscript.js[150, 18]La molla che porta il fascio verso il puntatore: quanto è rigida e quanto è frenata.
DRAWscript.js1.1I secondi che gli anelli impiegano a disegnarsi quando il menu si apre.

Provali nel Playground

Quando usarla

Per il menu di chi ha a che fare col mare, la navigazione e gli strumenti: un porto turistico, una scuola di vela, un cantiere, un'app per le maree o il meteo, uno studio che fa strumenti o software di rilevamento. Sta bene con quattro, cinque o sei voci corte su un fondo scuro; la nota sotto dice in due parole cosa c'è dietro.

Sul telefono

Lo schermo prende il diametro dalla larghezza e dall'altezza della finestra (fra 170 e 380 pixel), così pulsante e radar aperto stanno sempre dentro; la parola si rimpicciolisce con lo schermo e sotto i 300 pixel di diametro le note non ci sono. Le stesse animazioni: il fascio gira e accende le voci, e col dito sullo schermo lo si porta dove si vuole; il tocco su una voce la sceglie.

Con il movimento ridotto

Niente fascio che gira e niente disegno degli anelli: aprendo, lo schermo e tutte le voci compaiono in dissolvenza, già accesi a metà; sotto il puntatore o col fuoco una voce prende peso e larghezza. Chiudendo tutto sfuma.

Trappole

  • Con vector-effect: non-scaling-stroke insieme a pathLength il tratto si disegna a metà: lo spessore si calcola invece dal diametro dello schermo (200/d unità del viewBox) in una variabile CSS.
  • Le voci a sinistra del centro crescevano verso il bordo e uscivano dallo schermo: hanno la classe blip--left, la parola a sinistra della crocetta e il testo allineato a destra, così crescono verso il centro.
  • Una voce che il fascio non ha ancora trovato è invisibile ma restava cliccabile: finché non è stata vista ha pointer-events none.

Librerie

GSAP 3.15.0, Mona Sans (font variabile, con gli assi del peso e della larghezza) 5.3.0.

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

Collaudo

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