Back

So fügen Sie einen einfachen Schneefall-Effekt zu Ihrer Website hinzu

So fügen Sie einen einfachen Schneefall-Effekt zu Ihrer Website hinzu

Eine saisonale Weihnachtsanimation auf Ihrer Website kann Besucher begeistern, aber die meisten Tutorials ignorieren, was in der Produktion wirklich zählt: Performance, Barrierefreiheit und Benutzererfahrung. Sie möchten keine dekorative Hintergrundanimation, die Ihre Core Web Vitals beeinträchtigt oder Nutzer verärgert, die reduzierte Bewegungen bevorzugen.

Dieser Leitfaden zeigt Ihnen, wie Sie einen leichtgewichtigen Canvas-Schneefall-Effekt erstellen, der Benutzerpräferenzen respektiert, pausiert, wenn er nicht sichtbar ist, und nicht stört. Sie erfahren auch, wann ein einfacherer CSS-Schneefall-Effekt sinnvoll ist.

Wichtigste Erkenntnisse

  • Canvas skaliert zuverlässiger als DOM-basierte Ansätze, sobald Sie über eine kleine Handvoll Partikel hinausgehen
  • Respektieren Sie immer prefers-reduced-motion, um die Barrierefreiheitspräferenzen der Nutzer zu berücksichtigen
  • Verwenden Sie die Page Visibility API, um Animationen in Hintergrund-Tabs zu pausieren und Ressourcen zu sparen
  • Reines CSS-Schneefall funktioniert für minimale Implementierungen (5-10 Schneeflocken), skaliert aber nicht gut

Warum Canvas für JavaScript-Schneefall-Animation

DOM-basierte Ansätze erstellen individuelle Elemente für jede Schneeflocke. Das funktioniert für eine Handvoll Partikel, aber die Skalierung auf Dutzende oder Hunderte bedeutet ständige DOM-Manipulation, Layout-Neuberechnungen und Speicherdruck durch Elementerstellung und -entfernung.

Ein Canvas-Schneefall-Effekt zeichnet alles auf ein einzelnes Element. Sie kontrollieren die Render-Schleife, verwalten den Partikelzustand in einfachen Arrays und vermeiden DOM-Overhead vollständig. Sobald Sie über eine kleine Anzahl von Partikeln hinaus skalieren oder flüssigere Bewegungen wünschen, wird Canvas zur verlässlicheren Standardlösung.

Zu verstehende Kompromisse:

  • Canvas benötigt JavaScript – kein JS bedeutet keinen Schnee
  • Text innerhalb von Canvas ist für Screenreader nicht zugänglich (in Ordnung für rein dekorative Effekte)
  • CSS-Animationen sind nicht „kostenlos” – sie verbrauchen dennoch CPU/GPU-Ressourcen

Einrichten des Canvas-Elements

Positionieren Sie das Canvas hinter Ihrem Inhalt mit CSS:

#snowfall {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: -1;
}

Die Regel pointer-events: none stellt sicher, dass das Canvas niemals Scrollen, Klicken oder andere Benutzerinteraktionen blockiert.

<canvas id="snowfall" aria-hidden="true"></canvas>

Das Hinzufügen von aria-hidden="true" teilt assistiven Technologien mit, dass dieses rein dekorative Element ignoriert werden soll.

Aufbau der Animationsschleife

Hier ist eine minimale Implementierung mit angemessenen Schutzmechanismen:

const canvas = document.getElementById('snowfall');
const ctx = canvas.getContext('2d');
let flakes = [];
let animationId = null;

function resize() {
  const dpr = window.devicePixelRatio || 1;
  canvas.width = window.innerWidth * dpr;
  canvas.height = window.innerHeight * dpr;
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.scale(dpr, dpr);
}

function createFlake() {
  return {
    x: Math.random() * window.innerWidth,
    y: -10,
    radius: Math.random() * 3 + 1,
    speed: Math.random() * 1 + 0.5,
    opacity: Math.random() * 0.6 + 0.4
  };
}

function update() {
  ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
  
  if (flakes.length < 80 && Math.random() > 0.95) {
    flakes.push(createFlake());
  }
  
  flakes = flakes.filter(f => {
    f.y += f.speed;
    ctx.beginPath();
    ctx.arc(f.x, f.y, f.radius, 0, Math.PI * 2);
    ctx.fillStyle = `rgba(255, 255, 255, ${f.opacity})`;
    ctx.fill();
    return f.y < window.innerHeight + 10;
  });
  
  animationId = requestAnimationFrame(update);
}

resize();
window.addEventListener('resize', resize);

Dies behandelt hochauflösende Bildschirme korrekt, indem der Canvas-Puffer entsprechend der devicePixelRatio skaliert wird. Der Aufruf ctx.setTransform(1, 0, 0, 1, 0, 0) setzt die Transformationsmatrix zurück, bevor die neue Skalierung angewendet wird, und verhindert so kumulative Skalierung bei Fenstergrößenänderungen.

Wesentliche Performance- und Barrierefreiheits-Schutzmechanismen

Respektierung von Benutzerpräferenzen

Benutzer, die prefers-reduced-motion gesetzt haben, haben explizit um weniger Animation gebeten. Respektieren Sie das:

const reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
let prefersReduced = reducedMotionQuery.matches;

reducedMotionQuery.addEventListener('change', (e) => {
  prefersReduced = e.matches;
  if (prefersReduced) {
    cancelAnimationFrame(animationId);
    ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
  } else if (!document.hidden) {
    update();
  }
});

if (!prefersReduced) {
  update();
}

Diese Implementierung lauscht auf Änderungen der Bewegungspräferenz des Benutzers und ermöglicht es der Animation, dynamisch zu reagieren, wenn sich die Einstellung ändert, während die Seite geöffnet ist.

Pausieren bei Unsichtbarkeit

Animationen in Hintergrund-Tabs laufen zu lassen, verschwendet Akku und CPU. Die Page Visibility API löst dies:

document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    cancelAnimationFrame(animationId);
  } else if (!prefersReduced) {
    update();
  }
});

Wann CSS-Schneefall-Effekte sinnvoll sind

Für sehr kleine Implementierungen – vielleicht 5-10 Schneeflocken in einem Hero-Bereich – vermeidet ein reiner CSS-Ansatz JavaScript vollständig:

.snowflake {
  position: absolute;
  color: white;
  animation: fall 8s linear infinite;
}

@keyframes fall {
  to { transform: translateY(100vh); }
}

Dies funktioniert für minimale dekorative Anwendungsfälle, skaliert aber nicht. Jedes Element löst dennoch Compositing aus, und Sie verlieren die programmatische Kontrolle über Dichte und Verhalten.

Anpassungsoptionen

Passen Sie diese Werte an die Ästhetik Ihrer Website an:

  • Dichte: Ändern Sie die Obergrenze 80 und den Spawn-Schwellenwert 0.95
  • Geschwindigkeitsbereich: Modifizieren Sie die Berechnung Math.random() * 1 + 0.5
  • Größe: Passen Sie die Radiusberechnung an
  • Umfang: Zielen Sie auf einen bestimmten Container statt auf window.innerWidth/Height

Fazit

Eine Weihnachtsanimation auf der Website sollte das Erlebnis bereichern, ohne es zu beeinträchtigen. Verwenden Sie Canvas für skalierbare Performance, respektieren Sie prefers-reduced-motion, pausieren Sie, wenn die Seite nicht sichtbar ist, und halten Sie den Effekt nicht-interaktiv. Beginnen Sie mit konservativen Partikelzahlen und passen Sie basierend auf tatsächlichen Gerätetests an – nicht auf Annahmen darüber, was Browser bewältigen können.

Häufig gestellte Fragen

Eine gut implementierte Canvas-Animation hat minimale Auswirkungen auf die Core Web Vitals. Da Canvas auf ein einzelnes Element rendert und requestAnimationFrame verwendet, verursacht es keine Layout-Verschiebungen und blockiert nicht den Haupt-Thread. Halten Sie die Partikelzahlen angemessen (unter 100) und pausieren Sie die Animation, wenn der Tab verborgen ist, um gute Performance-Werte zu erhalten.

Ja. Fügen Sie jedem Schneeflocken-Objekt eine Drift-Eigenschaft hinzu und aktualisieren Sie die x-Position in Ihrer Animationsschleife zusammen mit der y-Position. Verwenden Sie Math.sin mit einem zeitbasierten Offset für natürlich aussehende Oszillation oder wenden Sie einen konstanten horizontalen Wert für stetigen Wind an. Randomisieren Sie Drift-Werte pro Flocke für Abwechslung.

Ersetzen Sie window.innerWidth und window.innerHeight durch die Dimensionen Ihres Ziel-Containers. Verwenden Sie getBoundingClientRect, um die Größe und Position des Containers zu erhalten. Ändern Sie das Canvas-CSS von position fixed zu position absolute und platzieren Sie es innerhalb Ihres Ziel-Container-Elements.

Dies geschieht, wenn die Canvas-Puffergröße nicht mit der Pixeldichte des Displays übereinstimmt. Die devicePixelRatio-Skalierung im Code-Beispiel behebt dies, indem ein größerer Canvas-Puffer erstellt und der Zeichnungskontext skaliert wird. Stellen Sie sicher, dass Sie diese Skalierung in Ihrer resize-Funktion anwenden und die Transformationsmatrix vor jeder Skalierungsoperation zurücksetzen.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay