
Brendi me ha pedido una animación de confetti con estilo “brutalism” para que combine bien con su diseño, así que, rápido, me puse a codear una versión bonita con canvas, pues me encanta trabajar con la etiqueta <canvas>
de HTML. 🤓
Sin embargo, a Brendi no le ha gustado mi versión con canvas, porque mi estrategia es una pincelada y no un rectángulo. 🫠
Entonces, pensé que podría intentar crear confeti solo con etiquetas <div>
y un poquito de CSS, y de paso podría practicar un poco más con la Animation_API
de la plataforma. 🔥👨🏻🚒🧯🔥
Así que, me emocioné lo suficiente y me puse a trabajar (a leer código de otros, obvio) y escribí un componente que llena tu pantalla con 80 papelitos que caen en un vaivén gracioso y girando elegantemente. 🍃
Te dejo el link para que lo veas
aquí.
En esta entrada te voy a mostrar las piezas de este componente. Si no quieres construirlo y prefieres solo usarlo, copialo de aquí.
Pero, si sientes curiosidad, pues vente conmigo…
¡Construyamos nuestra propia animación de confetti sin canvas! 🎊
Vamos a ponerlo todo en un componente
Como queremos reutilizar nuestro confetti por aquí y por allá, pues vamos a poner nuestro código en un componente de React, lo que nos facilitará poder emplearlo en cualquiera de nuestras rutas (estamos usando React Router para este proyecto).
Creamos pues nuestro archivo y nuestra función.
export const BrendisConfetti = ({ duration }: { duration?: number }) => { const len = 50; useEffect(() => { document.querySelectorAll(".confetti").forEach((element, i) => { const scale = Math.random() * 0.6 + 0.5; // puedes jugar con esto element.animate( { opacity: [scale], transform: [ `translate3d(${ (i / len) * 100 // x }vw,-5vh,0) scale(${scale}) rotate(0turn)`, `translate3d(${ (i / len) * 100 + 10 // x }vw, 105vh,0) scale(${scale}) rotate(${ Math.random() > 0.5 ? "" : "-" // izq o der }2turn)`, ], }, { duration: Math.random() * 3000 + 4000, // 4-7 iterations: Infinity, delay: (Math.random() * 7000), } ); }); }, []); return Array.from({ length: len/2 }).map((_, i) => ( <section key={i}> <div className="confetti"> <div className={cn("rotate", { yellow: i % 2 === 0, purple: i % 2 !== 0, })} /> </div> <div className="confetti"> <div className={cn("askew", { yellow: i % 2 === 0, purple: i % 2 !== 0, })} /> </div> </section> )); };
Observa en el JSX que colocamos un contenedor con dos elementos .confetti
, este elemento contiene dos hijos con las clases: rotate
y askew
. Estos nombres describen su comportamiento (su animación que estamos por crear) y añadimos también la clase del color en caso de que el index del elemento sea par o impar. ✅
Bueno Lo que sigue lo vamos a dividir en tres partes:
- Vamos a crear las clases CSS necesarias en un archivo aparte para poderlas importar fácilmente en el archivo del componente (esto, gracias a Vite)
- Luego, ya con los estilos en su lugar, añadiremos dos animaciones más con
@keyframes
- Finalmente, añadiremos algunos delays a diferentes elementos para crear más singularidad
Eso, no mucho más, nos ayudaremos con VanillaJS y algunos de mis número mágicos que colecciono. Bueno, basta de especular ¡es hora de crear nuestro archivo .css
!
Creando el archivo de estilos
He llamado a mi archivo brendisConfetti.css
y he colocado todo lo que necesitamos de una vez, obsérvalo un poco, que ya te lo explico.
/* Animación de confetti experimento */ .confetti { width: 1rem; height: 1rem; display: inline-block; position: absolute; top: 0rem; left: 0rem; z-index: 50; transform-style: preserve-3d; user-select: none; } .yellow { background: hsl(50deg 77% 68%); } .purple { background: #9870ed; } .confetti .rotate { animation: driftyRotate 1s infinite both ease-in-out; perspective: 1000; height: 2rem; border: 2px solid black; // <= brutalism } .confetti .askew { perspective: 600; transform: skewY(10deg); width: 1rem; height: 2rem; animation: drifty 1s infinite alternate both ease-in-out; border: 2px solid black; clip-path: polygon(evenodd, 0% 0%, 100% 0%, 100% 100%, 0% 100%, 0% 0%); } @keyframes drifty { 0% { transform: skewY(10deg) translate3d(-250%, 0, 0); } 100% { transform: skewY(-12deg) translate3d(250%, 0, 0); } } @keyframes driftyRotate { 0% { transform: rotateX(0); } 100% { transform: rotateX(359deg); } }
Podemos leer este archivo CSS en dos partes. Primero están los tamaños y colores de los dos tipos de confetti que tendremos, luego encontraremos las dos animaciones hechas con keyframes para hacer el movimiento de vaivén de nuestro papelito y su rotación.
Necesitamos que cada uno de los papelitos tenga su propio delay y su propia duración para sus animaciones, así que, con una utilidad llamada rand, vamos a generar diferentes tiempos.
const rand = (min = 0, max = (min = 0)) => Math.random() * (max - min) + min; return Array.from({ length: len }).map((_, i) => ( <section key={i}> <div className="confetti"> <div style={{ animationDuration: `${rand(0.6, 2.8)}s`, // experimenta con los tiempos }} className={cn("rotate")} > <div style={{ animationDuration: `${rand(1, 2)}s`, animationDelay: `${rand(1, 3)}s`, }} className={cn("askew", { yellow: i % 2 === 0, purple: i % 2 !== 0, })} /> </div> </div> </section> ));
A rand()
no la veas mucho, es solo una de muchas formas de generar un número random, leela con calma. 📖
Realmente eso es todo, tal vez, solo nos faltaría importar los estilos en el componente para que vayan con él donde quiera que se use:
import "~/styles/brendisConfetti.css"; // drifty animations export const BrendisConfetti = () => { const len = 80; // ...
Esto podemos hacerlo gracias a Vite, que es lo que usamos como bundler. Y ya está, te dejo enlaces a todo el código y a mi mayor referencia. Espero lo hayas disfrutado.
Te dejo una versión funcionando en codePen que no usa React 😉
Abrazo. Bliss. 🤓
Enlaces relacionados

Cómo Publicar tu proyecto Vite en Github Pages con el método más fácil
Checa este otro Post
