- canvas-confetti ist eine clientseitige Bibliothek, die canvasbasierte Confetti-Animationen auf Webseiten ausführt und sowohl die Installation über NPM als auch die direkte Einbindung per CDN unterstützt
- Die grundlegende
confetti()-API lässt sich mit einem einzigen Optionsobjekt für Partikelanzahl, Winkel, Streuung, Geschwindigkeit, Gravitation, Farben, Formen, Position, z-index usw. anpassen; in Umgebungen mit Promise-Unterstützung kann außerdem der Zeitpunkt des Animationsendes abgefragt werden
- Für Nutzer mit Reduced Motion gibt es die Option
disableForReducedMotion; ihr Standardwert ist aktuell false, könnte sich aber in einem künftigen Major Release ändern
- Es lassen sich benutzerdefinierte Formen auf Basis von SVG-Paths und Text erstellen; zusätzlich zu den Standardformen
square, circle und star sind auch Effekte wie Emoji-Confetti möglich
confetti.create() erstellt eine Instanz für ein bestimmtes Canvas und unterstützt globale Optionen wie resize und useWorker; bei useWorker: true wird die Kontrolle über das Canvas jedoch an einen Web Worker übertragen, sodass Manipulationen im Main Thread Fehler verursachen
Installation und Ausführung
- Auf der Demoseite kann die Funktionsweise der Bibliothek ausprobiert werden
- Die Installation als NPM-Paket ist möglich
npm install --save canvas-confetti
- Im Projekt-Build kann sie mit
require('canvas-confetti') verwendet werden
- Diese Bibliothek ist eine Client-Komponente und läuft nicht in Node
- Laut README sollte das Projekt mit einem Tool wie webpack gebaut werden
- In HTML-Seiten kann sie direkt per CDN-Skript eingebunden werden
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.4/…;
- Bei der Nutzung per CDN wird empfohlen, die zum Einbindungszeitpunkt aktuelle Version zu verwenden; alle Versionen sind auf der Releases-Seite verfügbar
Unterstützung für Reduced Motion
- Einige Nutzer möchten Bewegungen auf Websites nicht sehen oder bevorzugen weniger davon; Browser können dies über
prefers-reduced-motion mitteilen
- Mit der Option
disableForReducedMotion kann verhindert werden, dass potenziell irritierende Animationen Nutzern angezeigt werden, für die solche Effekte problematisch sind
- Der Standardwert dieser Option ist derzeit
false
- Für ein künftiges Major Release wird erwogen, den Standardwert zu ändern; starke Meinungen dazu können als Issue eingebracht werden
- Wenn durch
disableForReducedMotion das Confetti deaktiviert wird, wird das confetti()-Promise sofort aufgelöst
Grundlegende API und Promise-Verhalten
- Bei Installation über NPM kann die Bibliothek im Projekt-Build als Client-Komponente per
require genutzt werden; in der CDN-Version ist sie als confetti-Funktion auf window verfügbar
confetti([options]) nimmt ein optionales Optionsobjekt entgegen
- Falls
window.Promise vorhanden ist, wird ein Promise zurückgegeben, das das Ende der Animation signalisiert
- In Umgebungen ohne Promise wie IE wird
null zurückgegeben
- Es kann ein Promise-Polyfill verwendet werden
- Mit
confetti.Promise = MyPromise kann auch eine eigene Promise-Implementierung bereitgestellt werden
- Wenn
confetti vor Abschluss erneut mehrfach aufgerufen wird, wird jedes Mal dasselbe Promise zurückgegeben
- Intern wird dasselbe Canvas-Element wiederverwendet, und neues Confetti wird hinzugefügt, während bestehende Animationen weiterlaufen
- Die von den einzelnen Aufrufen zurückgegebenen Promises werden erst aufgelöst, wenn alle Animationen beendet sind
Wichtige Optionen
particleCount: Anzahl der abzufeuenden Confetti-Partikel, Standardwert 50
angle: Abschusswinkel, Standardwert 90; 90 bedeutet nach oben
spread: Streuungsbereich vom Zentrum aus, Standardwert 45
startVelocity: Anfangsgeschwindigkeit, Standardwert 45
decay: Stärke der Geschwindigkeitsabnahme, Standardwert 0.9
- Sollte zwischen 0 und 1 liegen; außerhalb dieses Bereichs kann sich die Geschwindigkeit erhöhen
gravity: Stärke, mit der Partikel nach unten gezogen werden, Standardwert 1
0.5 entspricht halber Gravitation; da es keine Begrenzung gibt, lassen sich Partikel auch nach oben steigen lassen
drift: Seitliche Drift, Standardwert 0
- Negative Werte bedeuten links, positive rechts
flat: Deaktiviert den Effekt, dass sich das Confetti wie reales 3D-Confetti neigt und flattert; Standardwert false
ticks: Anzahl der Bewegungszyklen des Confettis, Standardwert 200
origin: Startposition des Abschusses
origin.x: x-Position auf der Seite, 0 ist links, 1 ist rechts, Standardwert 0.5
origin.y: y-Position auf der Seite, 0 ist oben, 1 ist unten, Standardwert 0.5
colors: Array von Farbstrings im HEX-Format
shapes: Array von Confetti-Formen
- Standardmäßig eingebaut sind
square, circle, star
- Standard ist eine gleichmäßige Mischung aus square und circle
- Mit Array-Verhältnissen wie
['circle', 'circle', 'square'] lässt sich die Mischverteilung anpassen
scalar: Skalierung jedes Partikels, Standardwert 1
zIndex: Anzeigeebene des Confettis, Standardwert 100
disableForReducedMotion: Deaktiviert Confetti für Nutzer mit Reduced-Motion-Präferenz
Benutzerdefinierte Formen erstellen
confetti.shapeFromPath({ path, matrix? }) erstellt mit einem SVG-Path-String eine benutzerdefinierte Confetti-Form
- Für pathbasierte Formen gelten einige Einschränkungen
- Alle Paths werden als gefüllte Formen behandelt; Stroke-Paths sind nicht implementiert
- Paths sind auf eine einzelne Farbe beschränkt
- Jeder Path benötigt eine gültige Transform-Matrix
- Die Berechnung der Matrix ist aufwendig; während der Entwicklung sollte sie pro Path einmal berechnet und zwischengespeichert werden
- Für denselben Path-Wert ist die Matrix immer identisch
- Nach einem Bibliotheks-Update sollte die Matrix zur Wahrung der Forward-Kompatibilität neu erzeugt und gecacht werden
- Pathbasiertes Confetti ist auf Browser beschränkt, die
Path2D unterstützen
- Der Rückgabewert ist ein
Shape-Objekt, das direkt im shapes-Array verwendet werden kann
var triangle = confetti.shapeFromPath({ path: 'M0 10 L5 0 L10 10z' });
confetti({
shapes: [triangle]
});
confetti.shapeFromText({ text, scalar?, color?, fontFamily? }) erstellt textbasierte Confetti-Formen und kann Standard-Unicode-Emoji verwenden
- Textbasierte Formen eignen sich gut für Emoji-Confetti
- Für flatterndes Confetti eignen sich im Allgemeinen einzelne Zeichen mit annähernd quadratischer Form, besonders Emoji
- Der Text wird nicht bei jedem Frame neu gezeichnet, sondern rasterisiert; wenn die Skalierung nach dem Erstellen stark verändert wird, kann er unscharf werden
- Wenn die Option
scalar in den Confetti-Optionen verwendet werden soll, empfiehlt es sich, beim Erstellen der Form denselben scalar-Wert zu nutzen
- Die Textoptionen sind
text, scalar, color, fontFamily
- Der Standardwert von
fontFamily folgt den nativen Emoji-Rendering-Konventionen des Betriebssystems und verwendet sans-serif als Fallback
- Bei Webfonts muss die Schrift vor dem Confetti-Rendering geladen sein
var scalar = 2;
var pineapple = confetti.shapeFromText({ text: '🍍', scalar });
confetti({
shapes: [pineapple],
scalar
});
Benutzerdefiniertes Canvas und Rendering mit Worker
confetti.create(canvas, [globalOptions]) erstellt eine Instanz der Confetti-Funktion, die ein bestimmtes Canvas verwendet
- Das ist nützlich, wenn Confetti auf einen bestimmten Bereich der Seite beschränkt werden soll
- Standardmäßig verändert diese Methode das Canvas nicht, außer darauf zu zeichnen
- Wenn die sichtbare Größe des Canvas per CSS geändert wird, ändert sich die tatsächliche Bildgröße des Canvas nicht; das kann zu Streckung und Unschärfe führen
- Mit aktivierter Option
resize passt die Bibliothek die Bildgröße des Canvas an und reagiert auch auf Änderungen der Fenstergröße oder auf das Drehen mobiler Geräte
- Eine Confetti-Instanz sollte nicht mehrfach für dasselbe Canvas-Element initialisiert werden; die erzeugte benutzerdefinierte Instanz sollte beibehalten werden
-
Globale Optionen
resize: Legt fest, ob die Bildgröße des Canvas gesetzt und an Fensteränderungen angepasst werden soll; Standardwert false
useWorker: Rendert die Confetti-Animation, wenn möglich, in einem asynchronen Web Worker; Standardwert false
- Im Standardfall läuft die Animation immer im Main Thread
- Wenn der Browser es unterstützt, läuft die Animation außerhalb des Main Threads, damit dieser nicht blockiert wird
- In nicht unterstützenden Browsern wird dieser Wert ignoriert
disableForReducedMotion: Sorgt dafür, dass diese Confetti-Instanz die Reduced-Motion-Anforderung des Nutzers immer respektiert
-
Hinweise zu useWorker: true
- Bei
useWorker: true wird die Kontrolle über das Canvas an einen Web Worker übergeben
- In diesem Fall führt jede Manipulation im Main Thread außer dem Entfernen des Canvas aus dem DOM zu Fehlern
- Wenn das Canvas direkt manipuliert werden muss, sollte die Option
useWorker nicht verwendet werden
var myCanvas = document.createElement('canvas');
document.body.appendChild(myCanvas);
var myConfetti = confetti.create(myCanvas, {
resize: true,
useWorker: true
});
myConfetti({
particleCount: 100,
spread: 160
});
Animation stoppen und Beispielmuster
confetti.reset() stoppt die Animation, entfernt sämtliches Confetti und löst wartende Promises sofort auf
- Separate Instanzen, die mit
confetti.create() erstellt wurden, haben eine eigene Methode reset
confetti();
setTimeout(() => {
confetti.reset();
}, 100);
- Die Basisausführung erfolgt über den Aufruf
confetti() ohne Argumente
- Mit
particleCount: 150 kann eine große Menge Confetti abgeschossen werden
- Mit
spread: 180 lässt sich besonders breit gestreutes Confetti erzeugen
- Wird in
origin Math.random() verwendet, lassen sich kleine Explosionseffekte an zufälligen Positionen auf der Seite erzeugen
- Das README zeigt als Beispiel ein Muster, das mit
requestAnimationFrame 30 Sekunden lang fortlaufend Confetti vom linken und rechten Seitenrand aus abschießt
1 Kommentare
Hacker-News-Kommentare
Der Trick für performante Animationen hier besteht darin, auf ein Canvas zu zeichnen und dieses Canvas dann vor alle anderen Elemente zu legen, dabei aber Pointer Events zu deaktivieren, damit man weiter mit der Seite interagieren kann
Das erinnert mich an die gute alte Zeit, als ich 2015 in der Highschool Webentwicklung gemacht habe. Ich habe eine kleine Website mit Konfetti gebaut, um ein Mädchen zu fragen, ob sie mit mir zum Homecoming geht, und rückblickend war das extrem nerdig
Damals wirkte es auf ein Kind wie eine Superkraft, eine Website bauen zu können. Zeitlich war es wohl nicht dieses Paket, aber die Animation war ziemlich gut
Ich liebe solche kleinen Projekte, die einfach nur Freude machen. Deshalb habe ich überhaupt mit dem Programmieren angefangen, und bis heute ist das ein großer Antrieb
Diesen Teil der Demo-Seite mag ich:
Diese Liebe zum Detail ist selten, und ob bei statistischer Visualisierung, Filmrequisiten oder Website-Konfetti: Jedes Mal, wenn ich so etwas entdecke, weiß ich es zu schätzen
Als Lösung würde ich wahrscheinlich die Zufallsverteilung selbst ändern. Ich müsste es mir genauer ansehen, aber ich vermute, dass die reale Verteilung eher einer Gauß-Verteilung ähnelt
Wir haben Konfetti im Admin-Dashboard eingebaut, das erscheint, wenn ein Vertriebsmitarbeiter einen Abschluss macht, und es macht überraschend viel Spaß und motiviert
Ich wünschte, die reset-Funktion würde confetti.resetti() heißen
"confetti.resetti = confetti.reset"behebenDieser Ansatz hat zwar gewisse softwaretechnische Kosten, aber wie jeder aufmerksame Beobachter unschwer erkennen kann, überwiegt der Nutzen ganz eindeutig, also würde ich es einfach machen
Abgesehen davon, dass es eine coole und nützliche Bibliothek ist, ist das auch ein gutes Beispiel für ein Deep Module, wie John Ousterhout es in Philosophy of Software Design beschreibt
Die einfachste Version, also einfach Konfetti auszulösen, ist extrem leicht zu benutzen, aber wenn man sich die Optionen ansieht, bekommt man ziemlich viel dazu: Schnee, bestimmte Farben, verschiedene Konfetti-Effekte und mehr
Cool und beeindruckend
Gleichzeitig möchte ich nicht, dass das auf irgendeiner Website läuft, die ich benutze. Besonders bei Newsletter-Pop-ups oder wenn ich etwas in den Warenkorb lege, will ich nicht von Konfetti verfolgt werden
Seltsamerweise kann dieser Effekt ziemlich wirkungsvoll eingesetzt werden. Ob in dieser Vollbild-Variante, weiß ich nicht, aber in einer Projektmanagement-Software eines Kunden, den ich kürzlich besucht habe, wurde beim Schließen eines Elements der Button grün und bekam so einen Effekt
Es war subtil, aber auffällig genug, und nach dem Meeting meinten ein anderer Entwickler und ich beide: „War eigentlich ein ziemlich netter Effekt.“ Es vermittelte dieses Gefühl von „Gut, es geht voran!“
Man sollte es nur optional machen
Ein legitimer Einsatzort wäre wohl so etwas wie der Like-Button auf YouTube. Es gibt dort eine nette Animation, und in der mobilen App vibriert sogar das Gerät. Das ist eine sehr angenehme User Experience
Im Browser kann man einstellen, dass man reduzierte Bewegung bevorzugt. Website-Betreiber und Maintainer von Bibliotheken sollten das respektieren, wenn sie so etwas wie Konfetti implementieren. Diese Bibliothek hat dafür sogar die Option
disableForReducedMotionEs gibt Orte, an denen so ein Effekt passt. Zum Beispiel beim Abschluss eines Spiels
Wir verwenden diese Bibliothek, wenn jemand eine bestimmte Qualifikation erfüllt. Das gibt dem Onboarding-Flow einen ziemlich netten Effekt
Es gibt auch die Bibliothek Party.js: https://party.js.org/
Welche ist dann kleiner?
10.4 kB minifiziert, 4.2kB minifiziert + Gzip
https://bundlephobia.com/package/canvas-confetti@1.9.2
28.3kB minifiziert, 7.4kB minifiziert + Gzip
https://bundlephobia.com/package/party-js@2.2.0
Allerdings weiß ich nicht genau, wie bundlephobia arbeitet. Es zeigt möglicherweise nicht die endgültige Größe eines Pakets am besten an. Wahrscheinlich berücksichtigt es weder Code-Splitting noch das Importieren nur dessen, was man braucht. Ich sehe es eher als schnelle, grobe Übersicht
Nach Gzip scheint confetti ein paar KB zu gewinnen, also wenn man nicht unbedingt jedes einzelne KB herausquetschen muss, können beide sinnvoll sein, je nachdem, welche Funktionen man braucht
Das Skript im Original wirkt auf Mobilgeräten deutlich performanter
Die Bibliothek im Original wirkt deutlich performanter. Auf meinem alten Arbeitsrechner spüre ich bei Party.js schon nach drei Klicks eine leichte Verzögerung
Bei canvas-confetti setzt die Verzögerung erst ein, wenn ich ein paar Sekunden ohne Pause geklickt und wahrscheinlich über 30 Konfetti-Instanzen mit vielen Partikeln erzeugt habe
Ich löse Kreuzworträtsel auf downforacross.com, und wenn man ein Rätsel fertigstellt, erscheint Konfetti
Vielleicht könnten sie hier etwas von dem performanteren Code übernehmen, damit es sich leichter anfühlt
Aber außer bei „Spaß“-Seiten oder seltener Nutzung möchte ich nicht, dass solche Animationen überall auftauchen
Ich finde nicht, dass man useful extra in den Titel packen muss