- canvas-confetti est une bibliothèque côté client qui exécute des animations de confettis basées sur canvas sur une page web, avec prise en charge à la fois de l’installation via NPM et de l’inclusion directe par CDN
- L’API de base
confetti() permet, avec un seul objet d’options, d’ajuster le nombre de particules, l’angle, la dispersion, la vitesse, la gravité, les couleurs, les formes, la position, le z-index, etc., et dans les environnements prenant en charge Promise, il est possible de récupérer le moment où l’animation se termine
- Pour les utilisateurs ayant activé Reduced Motion, l’option
disableForReducedMotion est fournie ; sa valeur par défaut est actuellement false, mais cela pourrait changer dans une future version majeure
- Il est possible de créer des formes personnalisées basées sur des SVG Path et du texte, et d’implémenter des effets comme les emoji confetti en plus des formes intégrées
square, circle et star
confetti.create() crée une instance sur un canvas spécifique et prend en charge des options globales comme resize et useWorker, mais avec useWorker: true, le contrôle du canvas est transféré à un web worker et toute manipulation depuis le thread principal provoquera une erreur
Installation et modes d’exécution
- Le fonctionnement de la bibliothèque peut être vérifié sur la page de démonstration
- Installation possible en tant que package NPM
npm install --save canvas-confetti
- Dans un build de projet, elle peut être utilisée avec
require('canvas-confetti')
- Cette bibliothèque est un composant client et ne s’exécute pas dans Node
- Le README indique que le projet doit être buildé avec un outil comme webpack
- Dans une page HTML, elle peut être incluse directement via un script CDN
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.4/…;
- En cas d’utilisation via CDN, il est recommandé d’utiliser la dernière version disponible au moment de l’intégration dans le projet ; l’historique complet des versions est consultable sur la page des releases
Prise en charge de Reduced Motion
- Certains utilisateurs peuvent ne pas vouloir d’animations sur les sites web, ou préférer qu’elles soient réduites ; le navigateur peut transmettre cette préférence via
prefers-reduced-motion
- L’option
disableForReducedMotion permet de ne pas afficher de confettis aux utilisateurs pour qui des animations perturbantes peuvent poser problème
- La valeur par défaut de cette option est actuellement
false
- Un changement de cette valeur par défaut est envisagé pour une future version majeure, et les avis tranchés peuvent être remontés via une issue
- Si
disableForReducedMotion désactive les confettis, la Promise renvoyée par confetti() est résolue immédiatement
API de base et comportement des Promise
- Après installation via NPM, elle peut être importée comme composant client dans un build de projet, et dans la version CDN elle est exposée sous la forme de la fonction
confetti sur window
confetti([options]) accepte un unique objet d’options facultatif
- Si
window.Promise existe, la fonction renvoie une Promise signalant la fin de l’animation
- Dans des environnements comme IE sans Promise, elle renvoie
null
- Il est possible d’utiliser un polyfill Promise
- Il est aussi possible de fournir directement une implémentation Promise avec
confetti.Promise = MyPromise
- Si
confetti est appelé plusieurs fois avant la fin, la même Promise est renvoyée à chaque fois
- En interne, le même élément canvas est réutilisé, et de nouveaux confettis sont ajoutés en poursuivant l’animation existante
- La Promise renvoyée par chaque appel est résolue une fois toutes les animations terminées
Principales options
particleCount : nombre de confettis à lancer, valeur par défaut 50
angle : angle de lancement, valeur par défaut 90, où 90 signifie vers le haut
spread : amplitude de dispersion autour du centre, valeur par défaut 45
startVelocity : vitesse initiale, valeur par défaut 45
decay : vitesse de ralentissement, valeur par défaut 0.9
- Doit rester entre 0 et 1, sinon la vitesse peut augmenter
gravity : intensité avec laquelle les particules sont attirées vers le bas, valeur par défaut 1
0.5 correspond à une demi-gravité, et comme il n’y a pas de limite il est même possible de les faire monter
drift : dérive latérale, valeur par défaut 0
- Une valeur négative signifie vers la gauche, positive vers la droite
flat : permet de désactiver l’effet d’inclinaison et d’oscillation des confettis 3D réalistes, valeur par défaut false
ticks : nombre de mouvements des confettis, valeur par défaut 200
origin : position de départ du lancement
origin.x : position x sur la page, 0 à gauche, 1 à droite, valeur par défaut 0.5
origin.y : position y sur la page, 0 en haut, 1 en bas, valeur par défaut 0.5
colors : tableau de chaînes de couleur au format HEX
shapes : tableau des formes de confettis
- Les valeurs intégrées par défaut sont
square, circle, star
- Par défaut,
square et circle sont mélangés à parts égales
- Il est possible d’ajuster le ratio du mélange avec un tableau comme
['circle', 'circle', 'square']
scalar : échelle de chaque particule, valeur par défaut 1
zIndex : couche d’affichage des confettis, valeur par défaut 100
disableForReducedMotion : désactive les confettis pour les utilisateurs préférant Reduced Motion
Créer des formes personnalisées
confetti.shapeFromPath({ path, matrix? }) crée une forme de confetti personnalisée à partir d’une chaîne SVG Path
- Les formes basées sur Path ont quelques contraintes
- Tous les path sont traités comme des formes pleines, les stroke path ne sont pas implémentés
- Les path sont limités à une seule couleur
- Tous les path nécessitent une matrice de transformation valide
- Le calcul de la matrice ayant un coût, il est conseillé de la calculer une fois par path pendant le développement et de la mettre en cache
- La matrice est toujours identique pour une même valeur de path
- Lors d’une mise à jour de la bibliothèque, il est recommandé de régénérer et remettre en cache la matrice pour assurer la compatibilité future
- Les confettis basés sur Path sont limités aux navigateurs prenant en charge
Path2D
- La valeur de retour est un objet
Shape, qui peut être utilisé directement dans le tableau shapes
var triangle = confetti.shapeFromPath({ path: 'M0 10 L5 0 L10 10z' });
confetti({
shapes: [triangle]
});
confetti.shapeFromText({ text, scalar?, color?, fontFamily? }) crée des formes de confettis basées sur du texte, avec prise en charge des emoji Unicode standards
- Les formes basées sur du texte conviennent particulièrement aux emoji confetti
- Pour l’effet de confettis oscillants, les caractères uniques proches d’un carré, en particulier les emoji, fonctionnent généralement bien
- Comme le texte est rastérisé plutôt que redessiné à chaque fois, un fort changement d’échelle après création peut produire un rendu flou
- Si vous prévoyez d’utiliser l’option
scalar des confettis, il est préférable d’utiliser la même valeur scalar lors de la création de la forme
- Les options texte acceptent
text, scalar, color, fontFamily
- La valeur par défaut de
fontFamily suit les conventions natives de rendu des emoji du système d’exploitation, avec sans-serif en fallback
- En cas d’utilisation d’une webfont, celle-ci doit être chargée avant le rendu des confettis
var scalar = 2;
var pineapple = confetti.shapeFromText({ text: '🍍', scalar });
confetti({
shapes: [pineapple],
scalar
});
Canvas personnalisé et rendu via worker
confetti.create(canvas, [globalOptions]) crée une instance de la fonction confetti utilisant un canvas spécifique
- C’est utile lorsqu’on veut limiter les confettis à une zone précise de la page
- Par défaut, cette méthode ne modifie pas le canvas en dehors du dessin lui-même
- Si la taille d’affichage du canvas est modifiée via CSS, la taille réelle de l’image du canvas ne change pas, ce qui peut l’étirer et la rendre floue
- En activant l’option
resize, la bibliothèque ajuste la taille de l’image du canvas et suit également les changements de taille de fenêtre ou la rotation sur mobile
- Il ne faut pas initialiser plusieurs fois une instance de confetti sur le même élément canvas ; il faut conserver et réutiliser l’instance personnalisée créée
-
Options globales
resize : détermine s’il faut définir la taille de l’image du canvas et la maintenir synchronisée avec les changements de fenêtre, valeur par défaut false
useWorker : rend l’animation des confettis dans un web worker asynchrone quand c’est possible, valeur par défaut false
- Par défaut, l’animation s’exécute toujours sur le thread principal
- Si le navigateur le prend en charge, l’animation s’exécute hors du thread principal afin de ne pas le bloquer
- Dans les navigateurs non compatibles, cette valeur est ignorée
disableForReducedMotion : garantit que cette instance de confetti respecte toujours la demande Reduced Motion de l’utilisateur
-
Points d’attention pour useWorker: true
- Avec
useWorker: true, le contrôle du canvas est transféré à un web worker
- Dans ce cas, toute manipulation depuis le thread principal, à l’exception de la suppression du canvas du DOM, provoque une erreur
- Si le canvas doit être manipulé directement, il ne faut pas utiliser l’option
useWorker
var myCanvas = document.createElement('canvas');
document.body.appendChild(myCanvas);
var myConfetti = confetti.create(myCanvas, {
resize: true,
useWorker: true
});
myConfetti({
particleCount: 100,
spread: 160
});
Arrêt de l’animation et exemples de patterns
confetti.reset() arrête l’animation, efface tous les confettis et résout immédiatement toute Promise en attente
- Les instances séparées créées avec
confetti.create() disposent de leur propre méthode reset
confetti();
setTimeout(() => {
confetti.reset();
}, 100);
- L’exécution de base consiste à appeler
confetti() sans argument
- Il est possible de lancer beaucoup de confettis avec
particleCount: 150
spread: 180 permet de créer des confettis très dispersés
- En utilisant
Math.random() dans origin, on peut créer de petites explosions à des positions aléatoires sur la page
- L’exemple du README montre un pattern utilisant
requestAnimationFrame pour lancer en continu des confettis depuis les bords gauche et droit pendant 30 secondes
1 commentaires
Commentaires Hacker News
L’astuce pour faire ici une animation performante consiste à dessiner sur un canvas, puis à placer ce canvas devant tous les autres éléments tout en désactivant les pointer events pour pouvoir continuer à interagir avec la page
Ça me rappelle les bons vieux jours où je faisais du développement web au lycée, en 2015. J’avais créé un petit site web avec des confettis pour demander à une fille si elle voulait aller au bal de promo avec moi, et avec le recul c’était incroyablement geek
À l’époque, pour un gamin, créer un site web ressemblait à un super-pouvoir. Je ne pense pas que c’était ce package vu la période, mais l’animation était plutôt réussie
J’adore ce genre de petits projets faits juste pour le plaisir. C’est pour ça que j’ai commencé à programmer, et ça reste encore aujourd’hui une grande source de motivation
J’aime bien ce passage de la page de démo :
Ce genre d’obsession du détail est rare, et que ce soit dans une visualisation statistique, un accessoire de film ou des confettis sur un site web, je trouve ça toujours précieux quand j’en vois
Comme solution, j’aurais envie de modifier la distribution aléatoire elle-même. Je vérifierais en pratique, mais j’ai l’intuition qu’en réalité la distribution se rapproche davantage d’une distribution gaussienne
J’ai ajouté des confettis dans le tableau de bord admin quand un commercial conclut une vente, et c’est étonnamment amusant et motivant
J’aurais aimé que la fonction reset s’appelle confetti.resetti()
"confetti.resetti = confetti.reset"Cette approche a peut-être un petit coût en ingénierie logicielle, mais comme tout observateur attentif pourra le constater, les bénéfices l’emportent de façon écrasante, donc à mon avis il faut le faire
Indépendamment du fait que ce soit une bibliothèque sympa et utile, c’est un bon exemple de module profond au sens où John Ousterhout l’entend dans Philosophy of Software Design
La version la plus basique, c’est-à-dire déclencher des confettis, est très facile à utiliser, mais quand on regarde les options, on obtient pas mal de choses : neige, couleurs spécifiques, différents effets de confettis, etc.
C’est cool et impressionnant
En même temps, je n’ai aucune envie de voir ça se déclencher sur les sites web que j’utilise. Surtout pas pour une popup de newsletter ou quand j’ajoute un produit au panier
Curieusement, cet effet peut être utilisé de manière assez efficace. Je ne sais pas pour cette version plein écran, mais un logiciel de gestion de projet utilisé récemment par un client faisait passer le bouton au vert avec un effet de ce genre quand on clôturait un élément
C’était discret mais assez visible, et après la réunion un autre développeur et moi nous sommes tous les deux dit : « c’était plutôt un bon effet »
Ça donnait une sensation de « OK, il y a du progrès ! »
Il suffit simplement de le rendre optionnel
Un cas d’usage légitime serait peut-être le bouton J’aime de YouTube. Il y a une belle animation, et sur l’application mobile l’appareil vibre aussi. C’est une expérience utilisateur très satisfaisante
On peut configurer le navigateur pour indiquer une préférence pour la réduction des animations. Les exploitants de sites et les mainteneurs de bibliothèques devraient respecter ce choix lorsqu’ils implémentent des choses comme des confettis. Cette bibliothèque propose notamment l’option
disableForReducedMotionIl y a des endroits où ce genre d’effet a sa place. Par exemple à la fin d’un jeu
Nous utilisons cette bibliothèque quand quelqu’un remplit certains critères. Ça produit un effet plutôt sympa dans le parcours d’onboarding
Il y a aussi la bibliothèque Party.js : https://party.js.org/
Dans ce cas, laquelle est la plus légère ?
10.4 kB minifié, 4.2kB minifié + Gzip
https://bundlephobia.com/package/canvas-confetti@1.9.2
28.3kB minifié, 7.4kB minifié + Gzip
https://bundlephobia.com/package/party-js@2.2.0
Cela dit, je ne sais pas vraiment comment fonctionne bundlephobia. Ce n’est peut-être pas la meilleure représentation de la taille finale d’un package. J’imagine que ça ne prend probablement pas en compte le code splitting ou le fait de n’importer que ce dont on a besoin. Je l’utilise juste comme aperçu rapide et approximatif
En taille Gzip, confetti gagne de quelques kB, donc à moins de devoir vraiment gratter ces quelques kB, les deux peuvent convenir selon les fonctionnalités dont on a besoin
Le script de l’article original semble bien plus performant sur mobile
La bibliothèque de l’article original semble bien plus performante. Sur mon vieux PC de travail, Party.js commence à montrer un peu de latence après seulement 3 clics
canvas-confetti ne commence à ralentir qu’après plusieurs secondes de clics ininterrompus, quand on arrive probablement à plus de 30 instances de confettis et beaucoup de particules
Je fais des mots croisés sur downforacross.com, et quand on termine une grille il y a des confettis
Ils pourraient peut-être utiliser une partie du code plus performant présenté ici pour rendre ça plus léger
Mais en dehors d’un site « fun » ou d’un usage rare, je n’ai pas envie de voir ce genre d’animation partout
Je ne pense pas qu’il soit nécessaire d’ajouter useful dans le titre