CSS e web

Gli shader per gradienti WebGL, spiegati ai designer

Alcuni gradienti sul web scorrono, brillano e luccicano come nessuna immagine o regola CSS può fare. Sono shader. Ecco come funzionano, con parole che un designer può usare.

GradientlyAccount Gradiently verificato··7 min di lettura
Copertina: Thistle Mosaic · GR·2BAQ·CR

In breve

  • Un gradiente WebGL è disegnato da uno shader: un piccolo programma che gira sul chip grafico e decide il colore di ogni pixel, spesso molte volte al secondo.
  • Poiché uno shader ricava il colore dalla matematica invece di leggere un’immagine salvata, può aggiungere movimento, rumore, grana e luce a qualsiasi dimensione senza che il file diventi più pesante.
  • I gradienti shader costano più dei gradienti CSS da far girare, quindi uno ben fatto si mette in pausa quando è fuori dallo schermo, rispetta la riduzione del movimento e ha un’alternativa in CSS semplice.
  • Usa il CSS per sfumature statiche e movimenti semplici, e ricorri a uno shader quando texture, profondità o movimento sono il senso del design.
In questa pagina

Un gradiente WebGL è un gradiente che il browser dipinge con uno shader: un breve programma che gira sul chip grafico (la GPU) e risponde a una sola domanda per ogni pixel sullo schermo, «di che colore sei in questo momento?» Un gradiente CSS è una ricetta fissa di colori e posizioni. Uno shader è una formula, quindi può piegare il colore, dargli grana, farlo brillare e muoverlo, e rifarlo sessanta volte al secondo, senza scaricare alcuna immagine.

Non serve scrivere shader per progettare con loro, ma sapere cosa possono e non possono fare ti aiuta a scrivere un brief per uno sviluppatore, a giudicare uno strumento e a capire quando un semplice gradiente CSS è la scelta migliore.

Cos’è uno shader, in parole semplici

WebGL è il modo in cui il browser parla con la GPU, e ogni disegno WebGL usa due tipi di shader. Il vertex shader posiziona le forme; per un gradiente a tutto schermo è solo un rettangolo che copre la tela. Il fragment shader fa il lavoro interessante: gira una volta per pixel, riceve la posizione di quel pixel e il tempo attuale, e restituisce un colore.

Poiché la GPU esegue migliaia di questi minuscoli programmi in parallelo, una formula che sarebbe lenta nel codice normale diventa abbastanza economica da ridisegnare a ogni fotogramma. Ecco un fragment shader completo per un gradiente a tre colori il cui confine ondeggia lentamente nel tempo, con sopra una riga di grana.

text
#version 300 es
precision highp float;

uniform vec2 u_resolution;   // canvas size in pixels
uniform float u_time;        // seconds since start
out vec4 outColor;

void main() {
  vec2 uv = gl_FragCoord.xy / u_resolution;   // 0 to 1 across the canvas

  vec3 night  = vec3(0.07, 0.04, 0.18);  // #120a2e
  vec3 violet = vec3(0.49, 0.23, 0.93);  // #7c3aed
  vec3 coral  = vec3(0.98, 0.45, 0.45);  // #fa7373

  // A slow wave bends the blend so the colours drift.
  float wave = sin(u_time * 0.3 + uv.x * 3.0) * 0.08;
  float t = clamp(uv.y + wave, 0.0, 1.0);

  vec3 color = mix(night, violet, smoothstep(0.0, 0.6, t));
  color = mix(color, coral, smoothstep(0.55, 1.0, t));

  // Fine grain: a different random value per pixel, per frame.
  float n = fract(sin(dot(gl_FragCoord.xy + u_time, vec2(12.9898, 78.233))) * 43758.5453);
  color += (n - 0.5) * 0.04;

  outColor = vec4(color, 1.0);
}
Un fragment shader WebGL2 in GLSL. smoothstep ammorbidisce ogni transizione, l’onda sinusoidale sposta il confine e le ultime righe aggiungono una grana che nasconde anche il banding.
I tre colori dello shader. GLSL scrive il colore da 0 a 1 per canale, quindi #7c3aed diventa 124, 58 e 237 divisi per 255.

In cosa un gradiente shader differisce da un gradiente CSS

Un gradiente CSS è dichiarativo: elenchi colori e posizioni, e il browser li dipinge. Nella maggior parte dei casi è lo strumento giusto. Uno shader è procedurale: descrivi come si deve comportare il colore, e la matematica lo produce. Questa differenza si vede in ciò che ciascuno può fare e in quanto costa.

AspettoGradiente CSSGradiente shader
Definito daColori, punti e una direzioneUna formula eseguita per ogni pixel
MovimentoSpostando posizione, dimensione o proprietà personalizzate animateQualsiasi cosa esprimibile in matematica, a ogni fotogramma
Texture e granaServe uno strato in più, come un rumore SVGIntegrate, e possono muoversi
Costo di esecuzioneMolto basso una volta dipintoTiene occupata la GPU mentre è animato
Funziona senza JavaScriptSìNo: uno script prepara la tela
Funziona nelle emailIn parte, a seconda del clientNo
Nessuno dei due è migliore in assoluto. Ciascuno vince in un compito diverso.
L’alternativa in CSSlinear-gradient(180deg, #120a2e 0%, #4c1d95 40%, #7c3aed 62%, #fa7373 100%)
La stessa palette come gradiente CSS fermo. Ogni sfondo shader dovrebbe avere un’alternativa come questa, mostrata prima che lo shader parta e ogni volta che non può girare.

Perché i gradienti shader sembrano vivi

Tre tecniche fanno gran parte del lavoro, e sentirai gli sviluppatori chiamarle per nome. Il rumore è una casualità morbida: invece di una sfumatura dritta, il colore segue un paesaggio di colline dolci, ed è per questo che i gradienti shader sembrano organici e non geometrici. Il domain warping reimmette il rumore in se stesso, così il colore si piega e vortica come inchiostro nell’acqua. Il tempo fa scorrere l’intero paesaggio, trasformando un campo fermo in una deriva.

  • La grana può essere generata per pixel e per fotogramma, così luccica come una pellicola invece di restare ferma come una texture appoggiata sopra. Leggi gradienti granulosi.
  • La luce si può simulare con poche righe: un riflesso che segue il rumore si legge come lucentezza sulla seta o sul vetro.
  • La risoluzione è gratuita. La formula gira a qualunque dimensione abbia lo schermo, quindi un gradiente shader resta nitido su un telefono e su un monitor 4K con lo stesso codice.
  • Il banding è più facile da battere, perché lo shader può aggiungere un po’ di dithering prima che il colore venga arrotondato a 8 bit. Il banding nei gradienti spiega perché conta.

Usa il CSS quando

  • Il gradiente è fermo, o si muove in un solo modo semplice
  • Sta dietro molto testo in una pagina lunga
  • Deve funzionare con gli script disattivati, o nelle email
  • Ti serve in una riga di codice

Usa uno shader quando

  • Il movimento o la texture sono il senso del design
  • È un’unica area hero, non ogni sezione
  • Vuoi grana, lucentezza o flusso che il CSS non sa simulare
  • Puoi fornire un’alternativa in CSS e una pausa

Il costo di uno sfondo WebGL in movimento

Uno shader animato tiene la GPU al lavoro per tutto il tempo in cui è sullo schermo. Su un desktop si nota appena. Su un telefono meno recente può significare mani calde, batteria che si scarica e una pagina che scorre meno fluida. I costi crescono con il numero di pixel e con la complessità della formula, quindi uno shader a tutto schermo su un display ad alta densità è il caso più pesante. I gradienti rallentano un sito web? spiega come misurarlo.

  1. 1

    Mostra prima l’alternativa in CSS

    Dipingi subito il gradiente fermo, poi fai comparire in dissolvenza la tela quando lo shader ha disegnato il primo fotogramma. Nessuno deve vedere un riquadro vuoto.

  2. 2

    Controlla che WebGL sia disponibile

    Alcuni browser, dispositivi e impostazioni di privacy lo disattivano. Se il contesto non si può creare, tieni l’alternativa.

  3. 3

    Rispetta la riduzione del movimento

    Se il visitatore chiede meno movimento, disegna un solo fotogramma e fermati. Prefers reduced motion spiega l’impostazione.

  4. 4

    Metti in pausa quando non si vede

    Ferma il ciclo di animazione quando la tela esce dalla vista o la scheda è nascosta.

  5. 5

    Disegna a una risoluzione sensata

    I gradienti morbidi raramente hanno bisogno di ogni pixel del dispositivo. Disegnare a metà risoluzione e lasciare che il browser ingrandisca può ridurre molto il lavoro, con una sfocatura che non si distingue.

js
const canvas = document.querySelector('#hero-canvas')
const gl = canvas.getContext('webgl2')
const still = matchMedia('(prefers-reduced-motion: reduce)').matches
let visible = true

if (!gl) {
  canvas.remove() // the CSS gradient behind it stays
} else {
  new IntersectionObserver(([entry]) => {
    visible = entry.isIntersecting
  }).observe(canvas)

  const frame = (ms) => {
    if (visible && !document.hidden) draw(gl, ms / 1000) // your draw call
    if (!still) requestAnimationFrame(frame)
  }
  requestAnimationFrame(frame)
}
Le protezioni in poche righe: un’alternativa quando manca WebGL, un solo fotogramma con la riduzione del movimento e nessun disegno fuori dallo schermo o in una scheda nascosta.

Come riferimento, l’API WebGL su MDN documenta ogni chiamata usata qui, e caniuse segue il supporto di WebGL2 nei vari browser.

Progettare un gradiente shader: cosa mettere nel brief

Puoi dirigere uno shader senza leggere il GLSL. Scrivi il brief come faresti per un materiale, non per un campione di colore. Indica il colore di fondo e i tre-cinque colori che ci vivono sopra, in hex. Descrivi la struttura: pozze morbide, bande lunghe, seta piegata. Indica la velocità in secondi per ciclo invece di «lento», perché lento significa cose diverse per persone diverse. Di’ quanta grana vuoi e se deve muoversi. Infine, di’ dove starà il testo, così la parte più carica del campo resta lontana.

Come Gradiently disegna i Mark con gli shader

I Mark di Gradiently sono gradienti shader. Ogni Mark è una ricetta di colori, fino a tre materiali sovrapposti, luce, grana e movimento, e lo Studio lo disegna dal vivo in WebGL2 dietro qualsiasi cosa progetti. Poiché viene calcolato e non salvato, lo stesso Mark si rende nitido in qualsiasi formato delle piattaforme e si muove quando esporti un video in loop. Il renderer sa anche dove sono le tue parole, quindi può spostare dietro di esse la parte più calma del campo. Cos’è un Mark spiega la ricetta per intero.

Thistle Mosaic

GR·2BAQ·CR

Thistle Mosaic

Vedi questo Mark
Un Mark è una ricetta shader con un nome, un codice e un solo proprietario. La sua pagina lo mostra mentre viene reso dal vivo.

Le domande più frequenti

Che cos’è un gradiente WebGL?

È un gradiente disegnato da uno shader, un piccolo programma che gira sul chip grafico e calcola il colore di ogni pixel. Poiché è una formula, può muoversi, aggiungere grana e rendersi a qualsiasi dimensione.

Un gradiente shader fa male alle prestazioni?

Uno shader fermo o in pausa costa poco, ma uno animato tiene occupata la GPU mentre è sullo schermo. Mettilo in pausa quando è fuori dalla vista, disegnalo a una risoluzione sensata e tieni un’alternativa in CSS.

Posso creare un gradiente shader senza programmare?

Sì. Diversi strumenti di design generano sfondi shader a partire da controlli, e la Forge di Gradiently costruisce i Mark, che sono gradienti shader, da una frase o controllo dopo controllo.

I gradienti shader rispettano le impostazioni di riduzione del movimento?

Solo se sono costruiti per farlo. Controlla la media query prefers-reduced-motion in JavaScript e disegna un solo fotogramma fermo quando è attiva.

Che cos’è il GLSL?

Il GLSL è il linguaggio di shading usato da WebGL. I fragment shader scritti in GLSL decidono il colore di ogni pixel, e il browser li compila per il chip grafico.

Condividi questa guida

Scritto da GradientlyAccount Gradiently verificato

Il team dietro Gradiently, uno strumento di design costruito attorno ai Mark: gradienti vivi che fanno sembrare tuo tutto ciò che progetti.

Vedi il nostro profilo
Tutte le guide su CSS e web