CSS och webb

WebGL-gradientshaders förklarade för designers

Vissa gradienter på webben driver, glöder och skimrar på ett sätt ingen bild eller CSS-regel kan. De är shaders. Så fungerar de, med ord en designer kan använda.

GradientlyVerifierat Gradiently-konto··6 minuters läsning
Omslag: Thistle Mosaic · GR·2BAQ·CR

Kortversionen

  • En WebGL-gradient ritas av en shader: ett litet program som körs på grafikchippet och avgör färgen på varje pixel, ofta många gånger i sekunden.
  • Eftersom en shader räknar ut färg ur matematik i stället för att läsa en lagrad bild kan den lägga till rörelse, brus, korn och ljus i vilken storlek som helst utan att filen blir större.
  • Shadergradienter kostar mer att köra än CSS-gradienter, så en bra sådan pausar när den är utanför skärmen, respekterar minskad rörelse och har en enkel CSS-reserv.
  • Använd CSS för statiska övergångar och enkel rörelse, och ta till en shader när texturen, djupet eller rörelsen är poängen med designen.
På den här sidan

En WebGL-gradient är en gradient som din webbläsare målar med en shader: ett kort program som körs på grafikchippet (GPU:n) och svarar på en fråga för varje pixel på skärmen, ”vilken färg har du just nu?” En CSS-gradient är ett fast recept av färger och positioner. En shader är en formel, så den kan böja, ge korn åt, låta glöda och flytta färgen, och göra om det sextio gånger i sekunden, utan att någon bild laddas ner.

Du behöver inte skriva shaders för att designa med dem, men att veta vad de kan och inte kan hjälper dig att instruera en utvecklare, bedöma ett verktyg och avgöra när en vanlig CSS-gradient är det bättre valet.

Vad en shader är, med enkla ord

WebGL är webbläsarens sätt att tala med GPU:n, och varje WebGL-ritning använder två sorters shader. Vertexshadern placerar former; för en gradient i helskärm är det bara en rektangel som täcker duken. Fragmentshadern gör det intressanta jobbet: den körs en gång per pixel, tar emot pixelns position och den aktuella tiden och returnerar en färg.

Eftersom GPU:n kör tusentals av de här små programmen parallellt blir en formel som skulle vara långsam i vanlig kod billig nog att rita om varje bildruta. Här är en komplett fragmentshader för en trefärgsgradient vars gräns svajar mjukt över tid, med en rad korn ovanpå.

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);
}
En WebGL2-fragmentshader i GLSL. smoothstep mjukar upp varje övergång, sinusvågen flyttar gränsen och de sista raderna lägger till korn som också döljer bandning.
De tre färgerna i shadern. GLSL skriver färg som 0 till 1 per kanal, så #7c3aed blir 124, 58 och 237 delat med 255.

Hur en shadergradient skiljer sig från en CSS-gradient

En CSS-gradient är deklarativ: du listar färger och positioner, och webbläsaren målar dem. Det är rätt verktyg för det mesta. En shader är procedurell: du beskriver hur färg ska bete sig, och matematiken producerar den. Den skillnaden syns i vad var och en kan göra och vad var och en kostar.

AspektCSS-gradientShadergradient
Definieras avFärger, stopp och en riktningEn formel som körs per pixel
RörelseFlyttad position, storlek eller animerade egna egenskaperAllt du kan uttrycka i matematik, varje bildruta
Textur och kornBehöver ett extra lager, som SVG-brusInbyggt, och det kan röra sig
Kostnad att köraMycket låg när den väl är måladHåller GPU:n sysselsatt medan den animeras
Fungerar utan JavaScriptJaNej: ett skript ställer in duken
Fungerar i mejlDelvis, beroende på klientenNej
Ingen är bättre i allmänhet. Var och en vinner på ett annat jobb.
CSS-reservenlinear-gradient(180deg, #120a2e 0%, #4c1d95 40%, #7c3aed 62%, #fa7373 100%)
Samma palett som en stillastående CSS-gradient. Varje shaderbakgrund bör levereras med en reserv som den här, som visas innan shadern startar och närhelst den inte kan köras.

Därför känns shadergradienter levande

Tre tekniker gör det mesta av jobbet, och du kommer att höra utvecklare nämna dem. Brus är mjuk slumpmässighet: i stället för en rak övergång följer färgen ett landskap av mjuka kullar, vilket är varför shadergradienter ser organiska ut snarare än geometriska. Domain warping matar tillbaka brus i sig självt, så att färgen viks och virvlar som bläck i vatten. Tid glider hela landskapet framåt, vilket förvandlar ett stilla fält till en drift.

  • Korn kan genereras per pixel och per bildruta, så att det skimrar som film i stället för att sitta stilla som en textur lagd ovanpå. Se korniga gradienter.
  • Ljus kan fejkas med några rader: en högdager som följer bruset läses som glans på siden eller glas.
  • Upplösning är gratis. Formeln körs i den storlek skärmen har, så en shadergradient förblir skarp på en telefon och på en 4K-skärm med samma kod.
  • Bandning är lättare att slå, eftersom shadern kan lägga till lite dither innan färgen avrundas till 8 bitar. Gradientbandning förklarar varför det spelar roll.

Använd CSS när

  • Gradienten är stilla, eller rör sig på ett enkelt sätt
  • Den ligger bakom mycket text på en lång sida
  • Den måste fungera med skript avstängda, eller i mejl
  • Du behöver den på en rad kod

Använd en shader när

  • Rörelsen eller texturen är poängen med designen
  • Det är ett hero-område, inte varje sektion
  • Du vill ha korn, glans eller flöde som CSS inte kan fejka
  • Du kan ge en CSS-reserv och en paus

Kostnaden för en rörlig WebGL-bakgrund

En shader som animeras håller GPU:n igång så länge den syns på skärmen. På en dator märks det knappt. På en äldre telefon kan det betyda varma händer, ett batteri som töms och en sida som scrollar mindre mjukt. Kostnaderna växer med antalet pixlar och formelns komplexitet, så en shader i helskärm på en skärm med hög täthet är det tyngsta fallet. Gör gradienter en webbplats långsammare tar upp hur du mäter det.

  1. 1

    Visa CSS-reserven först

    Måla den stillastående gradienten direkt och tona sedan in duken när shadern har ritat sin första bildruta. Ingen ska se en tom ruta.

  2. 2

    Kontrollera att WebGL finns

    Vissa webbläsare, enheter och integritetsinställningar stänger av det. Om kontexten inte kan skapas, behåll reserven.

  3. 3

    Respektera minskad rörelse

    Om besökaren ber om mindre rörelse, rita en bildruta och stanna. Minskad rörelse förklarar inställningen.

  4. 4

    Pausa när den inte syns

    Stoppa animationsloopen när duken scrollar ut ur bild eller fliken är dold.

  5. 5

    Rendera i en rimlig upplösning

    Mjuka gradienter behöver sällan varje enhetspixel. Att rita i halv upplösning och låta webbläsaren skala upp kan minska arbetet mycket för en suddighet du inte kan skilja från originalet.

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)
}
Skyddsräckena på några rader: en reserv när WebGL saknas, bara en bildruta vid minskad rörelse och ingen ritning utanför skärmen eller i en dold flik.

Som referens dokumenterar WebGL API på MDN varje anrop som används här, och caniuse följer stödet för WebGL2 i olika webbläsare.

Designa en shadergradient: vad du ska ange

Du kan styra en shader utan att läsa GLSL. Instruera den som du skulle instruera ett material, inte ett färgprov. Ange bottenfärgen och de tre till fem färger som lever på den, i hex. Beskriv strukturen: mjuka pooler, långa band, veckat siden. Ange farten i sekunder per cykel i stället för ”långsam”, eftersom långsam betyder olika saker för olika människor. Säg hur mycket korn, och om det rör sig. Säg till sist var texten ska sitta, så att fältets livligaste del hålls borta från den.

Så ritar Gradiently Marks med shaders

Marks i Gradiently är shadergradienter. Varje Mark är ett recept av färger, upp till tre staplade material, ljus, korn och rörelse, och Studion ritar den live med WebGL2 bakom allt du designar. Eftersom den räknas ut i stället för att lagras renderas samma Mark skarpt i vilken plattformsstorlek som helst och rör sig när du exporterar en loopvideo. Renderaren vet också var dina ord finns, så den kan flytta fältets lugnaste del bakom dem. Vad är en Mark förklarar receptet i sin helhet.

Thistle Mosaic

GR·2BAQ·CR

Thistle Mosaic

Se den här Marken
En Mark är ett shaderrecept med ett namn, en kod och en ägare. Dess sida visar den renderas live.

Vanliga frågor

Vad är en WebGL-gradient?

Det är en gradient ritad av en shader, ett litet program som körs på grafikchippet och räknar ut färgen på varje pixel. Eftersom den är en formel kan den röra sig, lägga till korn och renderas i vilken storlek som helst.

Är en shadergradient dålig för prestandan?

En stillastående eller pausad shader kostar lite, men en som animeras håller GPU:n sysselsatt medan den syns på skärmen. Pausa den när den är utanför bild, rita i en rimlig upplösning och behåll en CSS-reserv.

Kan jag göra en shadergradient utan att koda?

Ja. Flera designverktyg genererar shaderbakgrunder från reglage, och Gradiently Forge bygger Marks, som är shadergradienter, av en mening eller reglage för reglage.

Respekterar shadergradienter inställningar för minskad rörelse?

Bara om de är byggda för det. Kontrollera mediefrågan prefers-reduced-motion i JavaScript och rita en enda stillbildsruta när den är satt.

Vad är GLSL?

GLSL är det shaderspråk WebGL använder. Fragmentshaders skrivna i det avgör färgen på varje pixel, och webbläsaren kompilerar dem för grafikchippet.

Dela den här guiden

Skriven av GradientlyVerifierat Gradiently-konto

Teamet bakom Gradiently, ett designverktyg byggt kring Marks: levande gradienter som får allt du gör att kännas som ditt.

Se vår profil