Vai al contenuto
animorio
EN
Torna alla libreria

La transizione del televisore

Transizioni, SVGGSAP, Canvas, Solo CSSMediaPeso compresso: Demo 16 KBGSAP 28 KBGratisScarica lo zip

Un vecchio televisore a tubo nel suo mobile di noce, con la manopola dei canali, l'altoparlante e le antenne a orecchie di coniglio. Al clic la manopola scatta, l'immagine si strizza in una riga e poi in un punto di luce che si spegne, passa un attimo di neve con le righe che scorrono e il canale nuovo si apre dal centro: meteo, notiziario, cartone animato e telequiz.

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, viewport-fit=cover">
  <title>Change the channel</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/jetbrains-mono@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="room" data-stage>

    <div class="tv" data-tv>
      <!-- the cabinet: walnut, the bezel, the dial, the volume knob, the speaker and the rabbit ears.
           The script draws it, wide on a computer and upright on a phone (the controls go under the screen) -->
      <svg class="cabinet" data-cabinet aria-hidden="true"></svg>

      <!-- the tube: four made-up channels, each one drawn in a 400 x 300 picture -->
      <div class="screen" data-screen>
        <div class="tube" data-tube>
          <div class="roll" data-roll>

            <!-- the weather -->
            <div class="channel" data-channel="Weather">
              <svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
                <defs>
                  <linearGradient id="wx-sky" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0" stop-color="#2463a8"/>
                    <stop offset="1" stop-color="#123563"/>
                  </linearGradient>
                </defs>
                <rect width="400" height="300" fill="url(#wx-sky)"/>
                <g class="wx-sea" stroke="#ffffff" stroke-opacity="0.07" stroke-width="1" fill="none">
                  <path d="M0 60H400M0 100H400M0 140H400M0 180H400M0 220H400M60 34V262M140 34V262M220 34V262M300 34V262M380 34V262"/>
                </g>
                <path d="M58 132C70 88 140 66 196 92C236 74 300 86 312 128C326 166 292 214 240 222C196 244 128 238 104 206C66 196 46 168 58 132Z" fill="#62b06b" stroke="#3d8a4b" stroke-width="2.5"/>
                <path d="M84 140C110 120 150 128 170 112" fill="none" stroke="#4f9a5a" stroke-width="2" stroke-linecap="round"/>
                <path d="M318 214C332 196 364 198 372 216C378 234 352 248 332 240C316 234 310 224 318 214Z" fill="#62b06b" stroke="#3d8a4b" stroke-width="2.5"/>

                <!-- Port Avery: sun -->
                <g data-wx-sun>
                  <g class="wx-rays" data-spin stroke="#ffd23f" stroke-width="4" stroke-linecap="round">
                    <path d="M130 92V84M130 136V144M108 114H100M152 114H160M114.4 98.4L108.8 92.8M145.6 129.6L151.2 135.2M114.4 129.6L108.8 135.2M145.6 98.4L151.2 92.8"/>
                  </g>
                  <circle cx="130" cy="114" r="16" fill="#ffd23f" stroke="#f29e1f" stroke-width="3"/>
                </g>
                <rect x="110" y="150" width="40" height="20" rx="6" fill="#0d2342"/>
                <text class="wx-temp" x="130" y="165" text-anchor="middle">21°</text>
                <text class="wx-town" x="130" y="184" text-anchor="middle">Port Avery</text>

                <!-- Kestle: rain -->
                <g data-bob>
                  <g class="wx-rain" stroke="#9fd2ff" stroke-width="2.5" stroke-linecap="round">
                    <path data-drops d="M228 132L224 142M240 132L236 142M252 132L248 142M228 152L224 162M240 152L236 162M252 152L248 162" stroke-dasharray="6 14"/>
                  </g>
                  <path d="M214 128C204 128 200 112 214 110C214 96 234 92 240 104C246 94 266 96 266 112C278 112 278 128 266 128Z" fill="#ffffff"/>
                </g>
                <rect x="220" y="164" width="40" height="20" rx="6" fill="#0d2342"/>
                <text class="wx-temp" x="240" y="179" text-anchor="middle">16°</text>
                <text class="wx-town" x="240" y="198" text-anchor="middle">Kestle</text>

                <!-- Wynmouth: sun behind a cloud -->
                <g data-bob-2>
                  <circle cx="352" cy="190" r="12" fill="#ffd23f" stroke="#f29e1f" stroke-width="2.5"/>
                  <path d="M330 206C322 206 320 194 330 193C331 183 345 181 350 189C355 182 368 185 367 196C376 196 376 206 367 206Z" fill="#ffffff"/>
                </g>
                <rect x="324" y="244" width="40" height="18" rx="6" fill="#0d2342"/>
                <text class="wx-temp" x="344" y="258" text-anchor="middle">18°</text>

                <rect width="400" height="36" fill="#f2c230"/>
                <text class="wx-title" x="16" y="25">WEATHER AT SIX</text>
                <text class="wx-day" x="384" y="24" text-anchor="end">TOMORROW</text>
                <rect y="264" width="400" height="36" fill="#0a1b33" fill-opacity="0.9"/>
                <text class="wx-line" x="16" y="287">SUNNY SPELLS, A SHOWER BY THE COAST</text>
              </svg>
            </div>

            <!-- the news -->
            <div class="channel" data-channel="News">
              <svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
                <defs>
                  <radialGradient id="nw-bg" cx="0.7" cy="0.3" r="0.9">
                    <stop offset="0" stop-color="#2f4690"/>
                    <stop offset="1" stop-color="#0c1430"/>
                  </radialGradient>
                  <clipPath id="nw-tick">
                    <rect x="76" y="270" width="324" height="30"/>
                  </clipPath>
                </defs>
                <rect width="400" height="300" fill="url(#nw-bg)"/>
                <g fill="none" stroke="#ffffff" stroke-opacity="0.1" stroke-width="1.5">
                  <circle cx="310" cy="110" r="70"/>
                  <ellipse cx="310" cy="110" rx="34" ry="70"/>
                  <ellipse cx="310" cy="110" rx="70" ry="24"/>
                  <path d="M240 110H380M310 40V180"/>
                </g>
                <g data-globe-sweep>
                  <ellipse cx="310" cy="110" rx="10" ry="70" fill="none" stroke="#ffffff" stroke-opacity="0.22" stroke-width="1.5" vector-effect="non-scaling-stroke"/>
                </g>
                <rect x="14" y="14" width="138" height="26" fill="#c8202f"/>
                <text class="nw-logo" x="24" y="32">EVENING BULLETIN</text>
                <rect x="158" y="14" width="40" height="26" fill="#f4f1ea"/>
                <text class="nw-live" x="178" y="32" text-anchor="middle">LIVE</text>

                <!-- the presenter -->
                <g data-anchor>
                  <path d="M118 266C122 214 158 196 200 194C242 196 278 214 282 266Z" fill="#262a35"/>
                  <path d="M178 196L200 236L222 196Z" fill="#f2efe8"/>
                  <path d="M196 204H204L208 236L200 246L192 236Z" fill="#c8202f"/>
                  <path d="M178 196L194 226L186 232L170 202Z M222 196L206 226L214 232L230 202Z" fill="#323746"/>
                  <rect x="190" y="172" width="20" height="26" fill="#cf966a"/>
                  <g data-head>
                    <ellipse cx="200" cy="146" rx="30" ry="36" fill="#e0a97e"/>
                    <ellipse cx="170" cy="150" rx="5" ry="8" fill="#d59b70"/>
                    <ellipse cx="230" cy="150" rx="5" ry="8" fill="#d59b70"/>
                    <path d="M168 140C164 106 190 98 206 102C226 104 238 118 232 142C228 124 214 118 196 122C182 124 172 130 168 140Z" fill="#3a2a22"/>
                    <path d="M184 134Q190 130 196 134M204 134Q210 130 216 134" stroke="#3a2a22" stroke-width="2.4" fill="none" stroke-linecap="round"/>
                    <g data-blink>
                      <ellipse cx="190" cy="146" rx="3" ry="3.4" fill="#1d1612"/>
                      <ellipse cx="210" cy="146" rx="3" ry="3.4" fill="#1d1612"/>
                    </g>
                    <path d="M198 150Q196 160 202 160" stroke="#b97d55" stroke-width="2" fill="none" stroke-linecap="round"/>
                    <ellipse data-mouth cx="200" cy="168" rx="7" ry="3" fill="#7a3a2c"/>
                  </g>
                </g>
                <path d="M30 244H370L392 300H8Z" fill="#17244f"/>
                <path d="M30 244H370" stroke="#6f86d6" stroke-width="2"/>

                <g class="nw-lower">
                  <rect x="18" y="206" width="76" height="24" fill="#c8202f"/>
                  <text class="nw-tag" x="56" y="222" text-anchor="middle">BREAKING</text>
                </g>
                <g class="nw-lower">
                  <rect x="94" y="206" width="290" height="24" fill="#f4f1ea"/>
                <text class="nw-head" x="104" y="222">TOWN CLOCK RUNS FIVE MINUTES FAST</text>
                </g>

                <rect y="270" width="400" height="30" fill="#f4f1ea"/>
                <g clip-path="url(#nw-tick)">
                  <g data-ticker>
                    <text class="nw-tick" x="86" y="290" data-tick>FERRY BACK ON TIME / MARKETS CALM / LIBRARY OPEN LATE ON FRIDAYS /</text>
                    <text class="nw-tick" x="86" y="290" data-tick>FERRY BACK ON TIME / MARKETS CALM / LIBRARY OPEN LATE ON FRIDAYS /</text>
                  </g>
                </g>
                <rect y="270" width="76" height="30" fill="#c8202f"/>
                <text class="nw-tag" x="44" y="290" text-anchor="middle">LATEST</text>
              </svg>
            </div>

            <!-- the cartoon -->
            <div class="channel" data-channel="Cartoon">
              <svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
                <defs>
                  <linearGradient id="tn-sky" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0" stop-color="#7fcdf2"/>
                    <stop offset="1" stop-color="#d4f3ff"/>
                  </linearGradient>
                </defs>
                <rect width="400" height="300" fill="url(#tn-sky)"/>
                <g transform="translate(62 60)">
                  <g data-spin-slow stroke="#ffb627" stroke-width="5" stroke-linecap="round">
                    <path d="M0 -40V-32M0 40V32M-40 0H-32M40 0H32M-28.3 -28.3L-22.6 -22.6M28.3 28.3L22.6 22.6M-28.3 28.3L-22.6 22.6M28.3 -28.3L22.6 -22.6"/>
                  </g>
                  <circle r="24" fill="#ffd23f" stroke="#ffb627" stroke-width="3"/>
                  <path d="M-9 -4Q-6 -8 -3 -4M3 -4Q6 -8 9 -4" stroke="#7a4a12" stroke-width="2.4" fill="none" stroke-linecap="round"/>
                  <path d="M-8 6Q0 13 8 6" stroke="#7a4a12" stroke-width="2.4" fill="none" stroke-linecap="round"/>
                </g>
                <g transform="translate(270 4)">
                  <g data-cloud fill="#ffffff">
                    <path d="M40 92C30 92 28 78 40 76C40 64 58 60 64 70C70 60 88 64 86 78C98 78 98 92 86 92Z"/>
                  </g>
                </g>
                <g data-cloud-2 fill="#ffffff" fill-opacity="0.85">
                  <path d="M206 64C199 64 197 54 206 53C207 45 219 43 223 50C227 44 239 47 237 56C245 56 245 64 237 64Z"/>
                </g>
                <path d="M-10 230C60 180 140 190 210 224C260 200 330 180 410 214V300H-10Z" fill="#8bd16a"/>
                <path d="M-10 262C80 236 200 246 280 262C330 252 380 248 410 254V300H-10Z" fill="#5fb34a"/>
                <g fill="#4a9a3a">
                  <path d="M70 252l4 -10l4 10Z M300 236l4 -10l4 10Z M340 260l4 -10l4 10Z"/>
                </g>

                <!-- Pebble -->
                <g data-pebble>
                  <path d="M262 226C258 200 280 186 302 188C326 190 338 206 334 226Z" fill="#a3acb8"/>
                  <path d="M270 210C276 198 290 194 302 196" stroke="#c9d0d9" stroke-width="4" fill="none" stroke-linecap="round"/>
                  <g data-pebble-eyes>
                    <ellipse cx="290" cy="210" rx="5" ry="6" fill="#ffffff"/>
                    <ellipse cx="308" cy="210" rx="5" ry="6" fill="#ffffff"/>
                    <circle cx="291.5" cy="211" r="2.6" fill="#1d1612"/>
                    <circle cx="309.5" cy="211" r="2.6" fill="#1d1612"/>
                  </g>
                  <path d="M294 220Q299 224 304 220" stroke="#5d6672" stroke-width="2" fill="none" stroke-linecap="round"/>
                </g>

                <!-- Pip -->
                <ellipse data-pip-shadow cx="160" cy="236" rx="22" ry="4" fill="#3c7a2c" fill-opacity="0.45"/>
                <g data-pip>
                  <g data-pip-body>
                    <circle cx="160" cy="214" r="21" fill="#ffd23f" stroke="#f0a91c" stroke-width="3"/>
                    <path d="M144 216Q140 208 148 206" stroke="#f0a91c" stroke-width="3" fill="none" stroke-linecap="round"/>
                    <circle cx="156" cy="207" r="3" fill="#1d1612"/>
                    <circle cx="168" cy="207" r="3" fill="#1d1612"/>
                    <path d="M160 212L172 215L160 219Z" fill="#ff7a2e"/>
                    <path d="M156 194Q160 186 164 194" stroke="#f0a91c" stroke-width="3" fill="none" stroke-linecap="round"/>
                  </g>
                </g>

                <g data-title-wobble>
                  <text class="tn-title" x="200" y="116" text-anchor="middle">PIP &amp; PEBBLE</text>
                </g>
              </svg>
            </div>

            <!-- the quiz -->
            <div class="channel" data-channel="Quiz">
              <svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
                <rect width="400" height="300" fill="#341a66"/>
                <g transform="translate(200 46)">
                  <g data-burst fill="#4d27a0">
                    <path data-burst-path d=""/>
                  </g>
                </g>
                <rect x="104" y="18" width="192" height="44" rx="22" fill="#170b36" stroke="#ffd23f" stroke-width="3"/>
                <rect data-chase x="104" y="18" width="192" height="44" rx="22" fill="none" stroke="#ffffff" stroke-width="4" stroke-dasharray="14 14" stroke-linecap="round"/>
                <text class="qz-title" x="200" y="47" text-anchor="middle">BRAIN WAVE</text>
                <clipPath id="qz-wave">
                  <rect x="128" y="62" width="144" height="16"/>
                </clipPath>
                <g clip-path="url(#qz-wave)">
                  <path data-wave d="M128 70Q138 64 148 70T168 70T188 70T208 70T228 70T248 70T268 70T288 70T308 70T328 70" fill="none" stroke="#ffd23f" stroke-width="2.5" stroke-linecap="round"/>
                </g>

                <rect x="28" y="84" width="344" height="50" rx="12" fill="#0f0a2a" stroke="#8f6bff" stroke-width="2.5"/>
                <text class="qz-q" x="200" y="114" text-anchor="middle">WHICH PLANET HAS THE MOST KNOWN MOONS?</text>

                <g class="qz-answers">
                  <path d="M40 166L52 148H184L196 166L184 184H52Z" fill="#0f0a2a" stroke="#8f6bff" stroke-width="2"/>
                  <text class="qz-a" x="118" y="171" text-anchor="middle">Mars</text>
                  <path data-pick d="M204 166L216 148H348L360 166L348 184H216Z" fill="#0f0a2a" stroke="#8f6bff" stroke-width="2"/>
                  <path data-pick-glow d="M204 166L216 148H348L360 166L348 184H216Z" fill="#ff9d2e" stroke="#ffd23f" stroke-width="2"/>
                  <text class="qz-a" x="282" y="171" text-anchor="middle">Saturn</text>
                  <path d="M40 210L52 192H184L196 210L184 228H52Z" fill="#0f0a2a" stroke="#8f6bff" stroke-width="2"/>
                  <text class="qz-a" x="118" y="215" text-anchor="middle">Venus</text>
                  <path d="M204 210L216 192H348L360 210L348 228H216Z" fill="#0f0a2a" stroke="#8f6bff" stroke-width="2"/>
                  <text class="qz-a" x="282" y="215" text-anchor="middle">Mercury</text>
                </g>

                <rect x="28" y="248" width="96" height="36" rx="8" fill="#170b36"/>
                <text class="qz-name" x="76" y="262" text-anchor="middle">ANNA</text>
                <text class="qz-score" x="76" y="279" text-anchor="middle">300</text>
                <rect x="276" y="248" width="96" height="36" rx="8" fill="#170b36"/>
                <text class="qz-name" x="324" y="262" text-anchor="middle">RUBEN</text>
                <text class="qz-score" x="324" y="279" text-anchor="middle">250</text>
                <circle cx="200" cy="266" r="17" fill="#170b36" stroke="#4a2f8a" stroke-width="5"/>
                <circle data-clock cx="200" cy="266" r="17" fill="none" stroke="#ffd23f" stroke-width="5" stroke-linecap="round" transform="rotate(-90 200 266)" pathLength="100" stroke-dasharray="100 100"/>
                <path data-clock-hand d="M200 266V255" stroke="#ffd23f" stroke-width="2.5" stroke-linecap="round"/>
              </svg>
            </div>

          </div>
          <div class="flash" data-flash></div>
        </div>
        <canvas class="snow" data-snow width="200" height="150"></canvas>
        <div class="spot" data-spot></div>
        <p class="osd" data-osd aria-hidden="true"></p>
        <div class="scan" data-scan></div>
        <div class="glass" data-glass></div>
        <button class="screen__hit" type="button" data-hit data-autoclic aria-label="Change the channel"></button>
      </div>

      <button class="dial" type="button" data-dial data-autoclic aria-label="Turn the channel dial"></button>
    </div>

    <p class="hint" data-hint>Turn the dial, or tap the screen</p>
    <p class="sr-only" aria-live="polite" data-live></p>
  </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.

46 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 transizione del televisore» da Animorio e adattala al mio sito

Cosa fa

In un soggiorno al buio, con la luce calda di una lampada dietro, sale con un piccolo rimbalzo un televisore di una volta: il mobile di noce con le venature, la cornice d'ottone intorno al tubo, una piastra color crema con la manopola dei canali, la manopola del volume e l'altoparlante a listelli, il marchio inventato Lumatone, le gambe svasate e le antenne a orecchie di coniglio che ondeggiano. Intorno alla manopola dei canali ci sono i loro nomi (WTHR, NEWS, TOON, QUIZ) e quello acceso è in rosso. Il televisore si accende: un punto di luce, poi una riga che si apre dal centro, e compare il meteo, con la scritta del canale che si batte in verde in alto a destra. Al clic sulla manopola, o sullo schermo, la manopola si carica un poco all'indietro e scatta alla tacca dopo con un rimbalzo, il mobile sobbalza e le antenne tremano. L'immagine si schiaccia in una riga orizzontale che si fa bianca, poi la riga si stringe in un punto che si spegne, una volta sola. Viene un attimo di neve, con due bande scure che scorrono verso il basso e uno strappo luminoso che sale; poi il canale nuovo si apre dal centro, prima largo e sottile, poi alto, con un lampo che si smorza e un piccolo salto verticale che si assesta. Ogni canale ha la sua vita: nel meteo il sole gira, la pioggia cade e le nuvole dondolano; nel notiziario il conduttore parla, sbatte gli occhi e annuisce, il globo dietro gira e le notizie scorrono in basso; nel cartone Pip, un pulcino rotondo, rimbalza schiacciandosi a terra mentre Pebble, un sasso con gli occhi, dondola e sbatte le palpebre; nel telequiz il fondo a raggi gira, l'onda sotto il titolo scorre, la risposta scelta lampeggia piano e l'orologio fa il suo giro. Col mouse il riflesso del vetro segue il puntatore e la manopola si gira un poco quando ci passi sopra.

Come funziona

Il mobile disegnato dallo script

Il televisore è un SVG che lo script scrive in due versioni: largo, con i comandi a destra dello schermo, e verticale, con i comandi sotto, scelto con matchMedia('(max-aspect-ratio: 4/5)') e rifatto quando il telefono si gira. Le venature del noce sono un feTurbulence stirato in orizzontale (baseFrequency 0.004 0.11) portato a strisce scure con feColorMatrix e ritagliato sulla sagoma del mobile. Lo schermo invece è un div HTML messo sopra il disegno con le sue misure in percentuale (variabili CSS --sx, --sy, --sw, --sh), così i canali restano nitidi a ogni grandezza.

La manopola e le antenne

La manopola è un gruppo SVG girato con l'attributo transform rotate(angolo cx cy): un oggetto con rot e hover, animato da GSAP, e una funzione che riscrive l'attributo a ogni fotogramma. Al clic va indietro di 7 gradi in 0,09 secondi, poi avanti di 90 con back.out: è lo scatto. L'angolo cresce sempre (90, 180, 270, 360...), così dopo il quiz torna al meteo girando nello stesso verso. Le antenne ondeggiano con un seno che si smorza: un oggetto con t e w, dove w (l'ampiezza) va a zero mentre t corre.

Riga, punto, buio

Tutti e quattro i canali stanno in un contenitore, il tubo. Per spegnere, GSAP lo schiaccia in altezza fino a scaleY 0.012 con power3.in, mentre un velo bianco dentro il tubo sale: la riga diventa luce. Poi lo stringe in larghezza fino a scaleX 0.015 e lo nasconde; al suo posto c'è un div rotondo con un gradiente radiale bianco e verde, che si gonfia per un attimo e poi si rimpicciolisce e si spegne in mezzo secondo. Una volta sola: niente che pulsi.

La neve

La neve è una canvas di 200 per 150 pixel stirata sullo schermo. A ogni fotogramma ogni pixel prende un grigio a caso (scritto in un Uint32Array sopra l'ImageData, molto più veloce che colore per colore), moltiplicato per due bande scure fatte con un seno che scende col tempo; una fascia sottile che sale è più chiara e spostata di lato, come uno strappo. Il disegno lo chiama l'onUpdate di un tween GSAP su un numero da 0 a 1, così la neve ha la sua durata nella timeline e si ferma da sola.

Il canale nuovo e la sua vita

Il canale nuovo si apre al contrario: il tubo parte da una riga, si allarga in 0,2 secondi e poi si alza con back.out, il velo bianco si smorza a scatti con dei keyframes e il contenuto scende da yPercent -16 con elastic.out, il salto verticale dei televisori di una volta. Ogni canale ha una timeline di sei secondi che si ripete, con dentro tutti i suoi piccoli movimenti (le rotazioni sono a 45 o 60 gradi, quando il disegno torna uguale): parte da capo quando il canale si accende e si ferma quando si spegne. La scritta del canale si batte lettera per lettera e poi svanisce.

Valori da cambiare

NomeFileDi partenzaCosa cambia
SPEEDscript.js1La velocità di tutto il cambio di canale: 2 è il doppio, 0.5 la metà.
COLLAPSEscript.js0.5I secondi che l'immagine impiega a strizzarsi in una riga e poi in un punto.
STATICscript.js0.7I secondi di neve fra un canale e l'altro.
ROLLscript.js1Quanto scorrono le bande scure nella neve e quanto salta l'immagine nuova (0 = ferma).
GLOWscript.js1Quanto sono luminosi la riga e l'ultimo punto di luce (0 = si spengono e basta).
SNAPscript.js1Quanto è forte lo scatto della manopola: il suo rimbalzo, il sobbalzo del mobile e il tremito delle antenne.
SCANLINESscript.js0.35Quanto si vedono le righe sul vetro (0 = niente righe).

Provali nel Playground

Quando usarla

Per passare da una sezione all'altra di un sito con un'aria anni Sessanta o Settanta: il portfolio di un videomaker, il sito di un festival di cinema o di un programma, una pagina di casting, il menu dei progetti di uno studio dove ogni progetto è un canale. Funziona anche come gioco o come carosello di notizie.

Sul telefono

La stessa animazione con le misure adattate: sullo schermo alto il mobile si alza e i comandi vanno sotto lo schermo, in fila (manopola dei canali, volume, altoparlante), così il tubo resta largo quasi quanto il telefono. Un tocco sulla manopola o sullo schermo cambia canale. Il riflesso che segue il puntatore c'è solo col mouse.

Con il movimento ridotto

Niente si sposta né si schiaccia: la manopola è già sulla tacca nuova, la neve ferma compare in dissolvenza sopra il canale, il canale cambia sotto e la neve svanisce; la scritta del canale compare e svanisce in dissolvenza. I canali restano fermi, senza i loro movimenti, e il televisore all'apertura compare in dissolvenza.

Trappole

  • I nomi dei canali a destra e a sinistra della manopola, centrati sul loro raggio, finivano sopra le tacche lunghe: ai lati il testo parte (o finisce) appena dopo le tacche, con text-anchor start ed end.
  • La registrazione del video dell'anteprima si ferma quando l'immagine torna uguale: le piccole animazioni di ogni canale stanno in una timeline sola di sei secondi che si ripete, con le rotazioni di 45 o 60 gradi (dove il sole e i raggi tornano identici) e la striscia delle notizie che scorre esattamente di una copia in sei secondi.
  • La striscia delle notizie va misurata dopo che il carattere è arrivato (document.fonts.ready): misurata prima, con il carattere di riserva, le due copie si sovrapponevano o lasciavano un buco.
  • Le rotazioni dentro un gruppo già spostato con translate si fanno con l'attributo transform rotate(angolo cx cy) scritto da un onUpdate: così il centro è proprio quello del disegno, senza dubbi su quale sistema di coordinate usi svgOrigin.
  • Un clic mentre il canale sta cambiando non si perde e non ne avvia due insieme: si mette in coda e parte appena il primo cambio finisce.

Librerie

GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0, JetBrains Mono (font variabile) 5.3.0.

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

Collaudo

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

Tutte le animazioni