- canvas-confetti es una biblioteca cliente para ejecutar animaciones de confetti basadas en canvas en páginas web, compatible tanto con instalación vía NPM como con inclusión directa mediante CDN
- La API básica
confetti() permite ajustar, con un único objeto de opciones, la cantidad de partículas, el ángulo, la dispersión, la velocidad, la gravedad, los colores, las formas, la posición, el z-index y más; en entornos con soporte para Promise, permite recibir el momento en que termina la animación
- Para usuarios con Reduced Motion, ofrece la opción
disableForReducedMotion; actualmente su valor predeterminado es false, pero podría cambiar en una futura versión mayor
- Permite crear formas personalizadas basadas en SVG Path y texto, y además de las formas predeterminadas
square, circle y star, también se pueden implementar efectos como emoji confetti
confetti.create() crea una instancia sobre un canvas específico y admite opciones globales como resize y useWorker, pero con useWorker: true el control del canvas se transfiere a un web worker, por lo que manipularlo desde el hilo principal provoca errores
Instalación y formas de ejecución
- Puedes ver el funcionamiento de la biblioteca en la página de demo
- Se puede instalar como paquete de NPM
npm install --save canvas-confetti
- En builds de proyecto se puede usar con
require('canvas-confetti')
- Esta biblioteca es un componente cliente y no se ejecuta en Node
- El README indica que el proyecto debe compilarse con herramientas como webpack
- En una página HTML se puede incluir directamente con un script desde CDN
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.4/…;
- Al usar CDN, se recomienda usar la versión más reciente disponible al momento de incluirla en el proyecto; la lista completa de versiones puede consultarse en la página de releases
Soporte para Reduced Motion
- Algunos usuarios pueden no querer movimiento en los sitios web o preferir reducirlo, y el navegador puede comunicarlo mediante
prefers-reduced-motion
- Con la opción
disableForReducedMotion, se puede evitar mostrar confetti a usuarios para quienes las animaciones confusas resultan difíciles
- El valor predeterminado actual de esta opción es
false
- Se está considerando cambiar el valor predeterminado en una futura versión mayor; si tienes una opinión firme, puedes comunicarla mediante un issue
- Si
disableForReducedMotion se aplica y el confetti queda deshabilitado, la Promise de confetti() se resuelve de inmediato
API básica y comportamiento de Promise
- Al instalarlo con NPM, se puede requerir como componente cliente en el build del proyecto, y en la versión CDN se expone como la función
confetti en window
confetti([options]) recibe un único objeto de opciones opcional
- Si existe
window.Promise, devuelve una Promise que notifica la finalización de la animación
- En entornos sin Promise, como IE, devuelve
null
- Se puede usar un polyfill de Promise
- También se puede proporcionar directamente una implementación de Promise con la forma
confetti.Promise = MyPromise
- Si se llama varias veces a
confetti antes de que termine, siempre devuelve la misma Promise
- Internamente reutiliza el mismo elemento canvas y agrega nuevo confetti mientras continúa la animación existente
- La Promise devuelta por cada llamada se resuelve después de que terminan todas las animaciones
Opciones principales
particleCount: cantidad de confetti a lanzar, valor predeterminado 50
angle: ángulo de lanzamiento, valor predeterminado 90; 90 significa hacia arriba
spread: rango de dispersión desde el centro, valor predeterminado 45
startVelocity: velocidad inicial, valor predeterminado 45
decay: grado en que disminuye la velocidad, valor predeterminado 0.9
- Debe mantenerse entre 0 y 1; fuera de ese rango, la velocidad puede aumentar
gravity: cuánto se arrastran las partículas hacia abajo, valor predeterminado 1
0.5 es media gravedad, y como no hay límite, también se puede hacer que suban
drift: cuánto se desplazan lateralmente, valor predeterminado 0
- Un valor negativo significa hacia la izquierda y uno positivo hacia la derecha
flat: permite desactivar el efecto de inclinación y bamboleo como el confetti 3D real; valor predeterminado false
ticks: cantidad de veces que se mueve el confetti, valor predeterminado 200
origin: posición inicial del lanzamiento
origin.x: posición x de la página; 0 es izquierda, 1 es derecha, valor predeterminado 0.5
origin.y: posición y de la página; 0 es arriba, 1 es abajo, valor predeterminado 0.5
colors: arreglo de cadenas de color en formato HEX
shapes: arreglo de formas de confetti
- Los valores integrados predeterminados son
square, circle y star
- Por defecto mezcla square y circle en partes iguales
- Es posible ajustar la proporción de mezcla según la proporción del arreglo, por ejemplo
['circle', 'circle', 'square']
scalar: escala de cada partícula, valor predeterminado 1
zIndex: capa de visualización del confetti, valor predeterminado 100
disableForReducedMotion: deshabilita el confetti para usuarios que prefieren Reduced Motion
Crear formas personalizadas
confetti.shapeFromPath({ path, matrix? }) crea una forma de confetti personalizada a partir de un SVG Path string
- Las formas basadas en Path tienen algunas restricciones
- Todos los paths se tratan como formas rellenas; los stroke paths no están implementados
- Los paths se limitan a un solo color
- Todos los paths necesitan una transform matrix válida
- Calcular la matrix tiene costo, por lo que conviene calcularla una vez por path durante el desarrollo y cachearla
- La matrix siempre es la misma para el mismo valor de path
- Al actualizar la biblioteca, conviene volver a generar y cachear la matrix para mantener la forward compatibility
- El confetti basado en path se limita a navegadores compatibles con
Path2D
- El valor devuelto es un objeto
Shape, que se puede insertar directamente en el arreglo shapes
var triangle = confetti.shapeFromPath({ path: 'M0 10 L5 0 L10 10z' });
confetti({
shapes: [triangle]
});
confetti.shapeFromText({ text, scalar?, color?, fontFamily? }) crea una forma de confetti basada en texto y puede usar emoji Unicode estándar
- Las formas basadas en texto son adecuadas para emoji confetti
- Para confetti que se bambolea, generalmente funcionan bien los caracteres individuales cercanos a un cuadrado, especialmente los emoji
- Como se rasteriza sin dibujar el texto en cada ocasión, si se cambia mucho la escala después de crear la forma, puede verse borrosa
- Si planeas usar
scalar en las opciones de confetti, conviene usar el mismo valor de scalar al crear la shape
- Las opciones de texto reciben
text, scalar, color y fontFamily
- El valor predeterminado de
fontFamily sigue las prácticas nativas del sistema operativo para renderizar emoji y usa sans-serif como fallback
- Al usar una fuente web, la fuente debe estar cargada antes de renderizar el confetti
var scalar = 2;
var pineapple = confetti.shapeFromText({ text: '🍍', scalar });
confetti({
shapes: [pineapple],
scalar
});
Canvas personalizado y renderizado con workers
confetti.create(canvas, [globalOptions]) crea una instancia de la función de confetti que usa un canvas específico
- Es útil cuando quieres limitar el confetti solo a una zona específica de la página
- De forma predeterminada, este método no modifica el canvas salvo por dibujar en él
- Aunque cambies el tamaño visible del canvas con CSS, el tamaño real de la imagen del canvas no cambia, por lo que puede estirarse y verse borroso
- Si activas la opción
resize, la biblioteca ajusta el tamaño de la imagen del canvas y responde a cambios de tamaño de la ventana o rotación en móviles
- No inicialices varias veces instancias de confetti con el mismo elemento canvas; debes conservar la instancia personalizada creada
-
Opciones globales
resize: decide si se debe establecer el tamaño de imagen del canvas y mantenerlo ajustado a los cambios de la ventana; valor predeterminado false
useWorker: si es posible, renderiza la animación de confetti de forma asíncrona en un web worker; valor predeterminado false
- Con el valor predeterminado, la animación siempre se ejecuta en el hilo principal
- Si el navegador lo soporta, la animación se ejecuta fuera del hilo principal para no bloquearlo
- En navegadores que no lo soportan, este valor se ignora
disableForReducedMotion: hace que esa instancia de confetti siempre respete la solicitud de Reduced Motion del usuario
-
Advertencias sobre useWorker: true
- Al usar
useWorker: true, el control del canvas se transfiere al web worker
- En ese caso, manipular el canvas desde el hilo principal, salvo quitarlo del DOM, provoca errores
- Si necesitas manipular directamente el canvas, no debes usar la opción
useWorker
var myCanvas = document.createElement('canvas');
document.body.appendChild(myCanvas);
var myConfetti = confetti.create(myCanvas, {
resize: true,
useWorker: true
});
myConfetti({
particleCount: 100,
spread: 160
});
Detener la animación y patrones de ejemplo
confetti.reset() detiene la animación, borra todo el confetti y resuelve de inmediato las Promise pendientes
- Las instancias separadas creadas con
confetti.create() tienen su propio método reset
confetti();
setTimeout(() => {
confetti.reset();
}, 100);
- La ejecución básica llama a
confetti() sin argumentos
- Con
particleCount: 150 se puede lanzar mucho confetti
- Con
spread: 180 se puede crear confetti con una dispersión amplia
- Si se usa
Math.random() en origin, se pueden crear pequeños efectos de explosión en posiciones aleatorias de la página
- El ejemplo del README muestra un patrón que usa
requestAnimationFrame para lanzar confetti continuamente desde los bordes izquierdo y derecho durante 30 segundos
1 comentarios
Opiniones en Hacker News
El truco para hacer animaciones con buen rendimiento aquí es dibujar en un canvas y luego poner ese canvas por delante de todos los demás elementos, pero desactivando los eventos de puntero para que se pueda seguir interactuando con la página.
Me recuerda los buenos tiempos de hacer desarrollo web en la secundaria, en 2015. Hice un pequeño sitio web con confeti para preguntarle a una chica si quería ir conmigo al homecoming; visto en retrospectiva, fue bastante nerd.
En ese entonces, para un niño, hacer un sitio web se sentía como un superpoder. Por la fecha, no creo que haya sido este paquete, pero la animación estaba bastante bien.
Me encantan estos pequeños proyectos puramente divertidos. Por eso empecé a programar, y sigue siendo una gran motivación para mí.
Me gusta esta parte de la página de demo:
Esta obsesión por el detalle es rara, y cada vez que la encuentro —ya sea en visualización estadística, utilería de cine o confeti en un sitio web— se siente valiosa.
Como solución, probaría cambiar la distribución aleatoria en sí. Habría que verificarlo, pero sospecho que la distribución en la vida real se parecería más a una distribución gaussiana.
Agregamos confeti en el panel de administración cuando un vendedor concreta una venta, y resulta sorprendentemente divertido y motivador.
Ojalá hubieran llamado a la función reset confetti.resetti().
"confetti.resetti = confetti.reset".Este enfoque tendrá un pequeño costo de ingeniería de software, pero, como cualquier observador cuidadoso puede ver claramente, los beneficios lo superan por mucho, así que diría que adelante.
Más allá de ser una biblioteca genial y útil, es un buen ejemplo de un módulo profundo, como lo describe John Ousterhout en Philosophy of Software Design.
La versión más básica —invocar confeti— es muy fácil de usar, pero si revisas las opciones puedes obtener bastantes cosas: nieve, colores específicos, distintos efectos de confeti, etc.
Genial e impresionante.
Al mismo tiempo, no quiero verlo ejecutándose en ningún sitio web que use. En especial, no quiero confeti acompañando un popup de newsletter o cuando agrego un producto al carrito.
Curiosamente, este efecto puede usarse de forma bastante efectiva. No sé sobre esta versión de pantalla completa, pero en un software de gestión de proyectos que usaba un cliente que visité recientemente, al cerrar un ítem el botón se ponía verde y aparecía un efecto de este tipo.
Era sutil, pero lo bastante visible, y después de la reunión otro desarrollador y yo dijimos algo como “ese efecto estuvo bastante bien”. Transmitía la sensación de “¡bien, hay avance!”.
Eso sí: basta con hacerlo opcional.
Un uso legítimo podría ser algo como el botón de “me gusta” de YouTube. Tiene una animación bonita y, en la app móvil, el dispositivo también vibra. Es una experiencia de usuario muy agradable.
En el navegador se puede configurar la preferencia de reducir movimiento. Los operadores de sitios y mantenedores de bibliotecas deberían respetarla al implementar cosas como confeti. Esta biblioteca, en particular, tiene la opción
disableForReducedMotion.Hay lugares donde un efecto así sí queda bien. Por ejemplo, al completar un juego.
Usamos esta biblioteca cuando alguien cumple ciertos requisitos. Le da un efecto bastante bueno al flujo de onboarding.
También existe la biblioteca Party.js: https://party.js.org/
Entonces, ¿cuál de las dos es más pequeña?
10.4 kB minificado, 4.2 kB minificado + Gzip
https://bundlephobia.com/package/canvas-confetti@1.9.2
28.3 kB minificado, 7.4 kB minificado + Gzip
https://bundlephobia.com/package/party-js@2.2.0
Dicho eso, no sé bien cómo funciona bundlephobia. Quizás no muestra de la mejor manera el tamaño final del paquete. Probablemente no refleje code splitting ni importar solo lo necesario. Lo tomo solo como una vista rápida y aproximada.
En términos de Gzip, parece que confetti gana por algunos KB, así que, salvo que realmente necesites exprimir esos pocos KB, ambas opciones sirven según cuál tenga las funciones que necesitas.
El script del artículo original parece tener mucho mejor rendimiento en móvil.
La biblioteca del artículo original parece tener un rendimiento mucho mejor. En mi vieja computadora de trabajo, con Party.js ya se siente un pequeño retraso después de apenas 3 clics.
Con canvas-confetti el retraso recién empieza cuando hago clic sin parar durante varios segundos y ya debo tener más de 30 instancias de confeti con muchas partículas.
Resuelvo crucigramas en downforacross.com, y cuando completas un puzzle aparece confeti.
Tal vez podrían usar parte de este código con mejor rendimiento para que se sienta más ligero.
Pero, salvo en sitios de “diversión” o usos poco frecuentes, no quiero ver este tipo de animaciones por todas partes.
No creo que haga falta poner useful en el título.