Skip to content

Instantly share code, notes, and snippets.

@josephdburdick
Created July 22, 2024 17:09
Show Gist options
  • Save josephdburdick/8e0fcaae53a1cd93cc2d28222133d019 to your computer and use it in GitHub Desktop.
Save josephdburdick/8e0fcaae53a1cd93cc2d28222133d019 to your computer and use it in GitHub Desktop.
Fun canvas confetti component
import React, { useEffect, useRef } from "react";
interface ConfettiParticle {
x: number;
y: number;
r: number;
d: number;
color: string;
tilt: number;
tiltAngleIncremental: number;
tiltAngle: number;
draw: (context: CanvasRenderingContext2D) => void;
}
const ConfettiComponent: React.FC = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const particles = useRef<ConfettiParticle[]>([]);
const maxConfettis = 150;
const possibleColors = [
"DodgerBlue",
"OliveDrab",
"Gold",
"Pink",
"SlateBlue",
"LightBlue",
"Gold",
"Violet",
"PaleGreen",
"SteelBlue",
"SandyBrown",
"Chocolate",
"Crimson"
];
const randomFromTo = (from: number, to: number): number => {
return Math.floor(Math.random() * (to - from + 1) + from);
};
class Particle implements ConfettiParticle {
x: number;
y: number;
r: number;
d: number;
color: string;
tilt: number;
tiltAngleIncremental: number;
tiltAngle: number;
constructor() {
this.x = Math.random() * window.innerWidth;
this.y = Math.random() * window.innerHeight - window.innerHeight;
this.r = randomFromTo(11, 33);
this.d = Math.random() * maxConfettis + 11;
this.color = possibleColors[Math.floor(Math.random() * possibleColors.length)];
this.tilt = Math.floor(Math.random() * 33) - 11;
this.tiltAngleIncremental = Math.random() * 0.07 + 0.05;
this.tiltAngle = 0;
}
draw(context: CanvasRenderingContext2D) {
context.beginPath();
context.lineWidth = this.r / 2;
context.strokeStyle = this.color;
context.moveTo(this.x + this.tilt + this.r / 3, this.y);
context.lineTo(this.x + this.tilt, this.y + this.tilt + this.r / 5);
context.stroke();
}
}
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const context = canvas.getContext("2d");
if (!context) return;
const draw = () => {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
particles.current.forEach((particle) => particle.draw(context));
particles.current.forEach((particle, i) => {
particle.tiltAngle += particle.tiltAngleIncremental;
particle.y += (Math.cos(particle.d) + 3 + particle.r / 2) / 2;
particle.tilt = Math.sin(particle.tiltAngle - i / 3) * 15;
if (particle.x > window.innerWidth + 30 || particle.x < -30 || particle.y > window.innerHeight) {
particle.x = Math.random() * window.innerWidth;
particle.y = -30;
particle.tilt = Math.floor(Math.random() * 10) - 20;
}
});
requestAnimationFrame(draw);
};
const handleResize = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
window.addEventListener("resize", handleResize);
handleResize();
particles.current = [];
for (let i = 0; i < maxConfettis; i++) {
particles.current.push(new Particle());
}
draw();
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return <canvas ref={canvasRef} />;
};
export default ConfettiComponent;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment