Animerede favicons: få browserfanen til at bevæge sig (med live-demo)
Du har sandsynligvis lagt mærke til, at noget bevægede sig øverst i denne fane mens du læste. Det er en animeret favicon — det samme trick, Gmail bruger til at tælle nye mails, og Discord bruger til notifikationsprikker, bortset fra at din kan gøre stort set alt, hvad du kan tegne på en canvas.
Denne artikel indeholder en live-demo, du kan rode med. Klik på en knap, og se din rigtige browserfane ændre sig. Ingen screenshots, ingen indlejrede videoer — favicon:en, du kigger på lige nu, bliver demoen.
Hvorfor overhovedet animere en favicon?
Helt ærligt burde de fleste sites lade være. Et roterende ikon i hver fane bliver hurtigt irriterende og spiser CPU. Men der er nogle få tilfælde, hvor det faktisk er nyttigt:
- Loading- eller behandlingstilstande. Lange uploads, eksporter eller builds. Brugere skifter fane mens de venter, og en animeret favicon fortæller dem, at arbejdet stadig kører.
- Notifikations-badges. Nye beskeder, omtaler, alarmer. En rød prik, der pulserer diskret, opdages hurtigere end en statisk.
- Live-data-feeds. Trading-dashboards, monitoreringsværktøjer, sportsresultater — alle steder, hvor fanetitlen ikke er nok.
- Brand-øjeblikke. En højtidsspinner, en lanceringsdag-fejring. Brug sparsomt.
Hvis dit tilfælde ikke er ét af de ovenstående, så drop animationen. En god statisk SVG-favicon vinder allerede på filstørrelse, dark mode og batteritid.
Live-demo: prøv den nu
Vælg en animation. Kig så på din browserfane — det lille ikon deroppe er det, der bliver tegnet om.
Den rigtige favicon er 16×16 pixels. Detaljer er svære at se i den størrelse, så feltet til venstre spejler den samme canvas i 8× med nearest-neighbor-skalering.
Status: i hvile
Animationsloopen kører nu helt inde i en Web Worker, akkurat som favicon_worker.js i Aymkdn-biblioteket. Hver 20. ms (50 fps) tegner workeren på sin OffscreenCanvas, eksporterer via convertToBlob + FileReader og sender den resulterende data-URL tilbage til siden. Hovedtråden gør én ting: tildeler den streng til faviconLink.href. Det er derfor ikonet nu bevæger sig lige så jævnt som GitHub-demoen.
Vil du se mønsteret i et rigtigt produkt? Random Picker Wheel bruger en animeret favicon, der følger det roterende hjul — en rotationsbaseret UI er et af de få tilfælde, hvor et bevægeligt ikon faktisk passer til produktet. Åbn siden, snurr hjulet, og se hvordan fanens ikon snurrer med.
Hvordan det rent faktisk fungerer
Tre trin. Det er hele teknikken:
// 1. Find eller opret favicon-link-elementet
let link = document.querySelector('link[rel~="icon"]');
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.head.appendChild(link);
}
// 2. Tegn et frame på en skjult canvas
const canvas = document.createElement('canvas');
canvas.width = 32;
canvas.height = 32;
const ctx = canvas.getContext('2d');
function drawFrame(t) {
const scale = 0.5 + 0.5 * Math.abs(Math.sin(t / 400));
ctx.clearRect(0, 0, 32, 32);
ctx.fillStyle = '#ef4444';
ctx.beginPath();
ctx.arc(16, 16, 14 * scale, 0, Math.PI * 2);
ctx.fill();
// 3. Eksportér canvas til data-URL og tildel favicon:en
link.href = canvas.toDataURL('image/png');
requestAnimationFrame(drawFrame);
}
requestAnimationFrame(drawFrame);
Cirka 15 linjer for en fungerende pulserende prik. Alt mere fancy er bare at tegne andre former på samme canvas.
Virkeligheden bag browser-understøttelse
Her bliver det grimt. Browsere varierer en hel del i, hvor aggressivt de animerer favicon:
- Firefox: animerer jævnt, selv når fanen ikke har fokus. Den er guldstandarden.
- Chrome / Edge: animerer mens fanen er aktiv. Skifter du væk, throttler
requestAnimationFramened til omkring én gang i sekundet, så animationen sløver eller pauser. - Safari: animerer i fokus, men opdaterer nogle gange kun ikonet med langsomme intervaller. Regn ikke med jævn bevægelse.
Det er faktisk fint til de mest almindelige brugsscenarier — notifikationsprikker og forløbstilstande behøver alligevel kun at opdatere cirka én gang i sekundet. Jævne 60 fps-spinnere er for det meste kosmetik.
Web Worker-tricket (til baggrundsfaner)
Knappen "GitHub-stil flip" ovenfor er en direkte port af signaturanimationen fra biblioteket Aymkdn/animated-favicon: hold ikon A i 3 sekunder, komprimér dets bredde med cosinus til nul, skift til ikon B, og udvid det tilbage. Matematikken kommer direkte fra bibliotekets favicon_worker.js — width = canvas.width * Math.abs(Math.cos(progress * Math.PI)), hvor det andet billede tager over, så snart progress krydser 0,5.
Aymkdns bibliotek går et skridt videre, end vi gør her: det kører den loop inde i en Web Worker. Workers throttles ikke, når fanen er i baggrunden, så animationen fortsætter, og OffscreenCanvas lader workeren rendre frames uden at røre DOM:en.
Mønsteret ser sådan her ud:
// I din side
const worker = new Worker('favicon-worker.js');
worker.onmessage = (e) => {
if (e.data.type === 'updateFavicon') {
document.querySelector('link[rel~="icon"]').href = e.data.dataUrl;
}
};
worker.postMessage({ type: 'init', images: ['icon-a.png', 'icon-b.png'] });
// I favicon-worker.js
const canvas = new OffscreenCanvas(16, 16);
const ctx = canvas.getContext('2d');
// ...tegn et frame...
const blob = await canvas.convertToBlob();
const reader = new FileReader();
reader.onloadend = () => self.postMessage({ type: 'updateFavicon', dataUrl: reader.result });
reader.readAsDataURL(blob);
Værd umagen, hvis din app er den slags brugere parkerer i en baggrundsfane — chatklienter, build-dashboards, monitoreringsværktøjer. Ellers er hovedtråd-versionen enklere og god nok.
Et par ting, det er værd at vide
Brug 16×16 eller 32×32, ikke større. Favicon:en renderes alligevel meget lille, og større canvas betyder længere data-URL og mere CPU pr. frame. 32×32 med skarpe pixels er det rette sted.
Sæt favicon som PNG, ikke ICO. canvas.toDataURL('image/png') er det eneste, der virker pålideligt. Forsøg ikke at kode ICO selv.
Genskab den oprindelige favicon, når du stopper. Gem link.href før animationen starter, og gendan den ved beforeunload eller når operationen slutter. Faner, der bliver ved at vise en halvt brudt animation efter navigering, virker buggy.
Animér ikke i evigheder. Selv en diskret puls trækker mobilbatteri. Stop animationen, når load-tilstanden slutter, når brugeren har læst notifikationen, eller når fanen mister fokus.
Spring over for simple tilfælde. Vil du blot vise "1 ulæst besked", så skift favicon til en statisk rød-prik-version. Ingen animation nødvendig. Det meste, folk bruger animerede favicons til, er overkill.
Hvornår det giver mening
Animerede favicons passer godt, når:
- Animationen afspejler en reel tilstand, brugeren bekymrer sig om (uploader, behandler, ny besked)
- Siden er den slags, der lever i en baggrundsfane
- Et statisk badge eller blot en ændret fanetitel ikke ville formidle det samme
De passer dårligt, når:
- Det er ren dekoration
- Animationen kører hele den tid, fanen er åben
- Du retter dig mod brugere på Safari og mobil, hvor det knap virker
Tag demoen ovenfor med ind i dit eget projekt, byt farver og former ud med dit brand, og send det. Hele kildekoden er lige her på siden — view source, kopiér, tilpas.
Referencer
- Aymkdn/animated-favicon på GitHub — Web Worker-baseret animeret favicon-bibliotek, der bliver ved med at animere i inaktive faner
- The Making of an Animated Favicon — CSS-Tricks — Chris Coyiers gennemgang af canvas-til-favicon-teknikken
- How to animate a favicon? — Stack Overflow — den klassiske tråd med flere tilgange og noter om browser-understøttelse
- OffscreenCanvas — MDN — det API, der gør Web Worker-animationsmønsteret muligt
Use favicon.im to quickly check if your favicon is configured correctly. Our free tool ensures your website's favicon displays properly across all browsers and devices.
Free Public Service
Favicon.im is a completely free public service trusted by developers worldwide.