```html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>烟花特效</title>
<style>
body {
margin: 0;
overflow: hidden;
}
canvas {
display: block;
}
</style>
</head>
<body>
<canvas id="fireworks"></canvas>
<script>
const canvas = document.getElementById('fireworks');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
class Firework {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
this.particles = [];
for (let i = 0; i < 50; i++) {
this.particles.push(new Particle(this.x, this.y, this.color));
}
}
update() {
this.particles.forEach((particle, index) => {
if (particle.alpha <= 0) {
this.particles.splice(index, 1);
} else {
particle.update();
}
});
}
draw() {
this.particles.forEach(particle => particle.draw());
}
}
class Particle {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
this.size = Math.random() * 5 + 1;
this.speedX = Math.random() * 3 - 1.5;
this.speedY = Math.random() * 3 - 1.5;
this.alpha = 1;
}
update() {
this.x += this.speedX;
this.y += this.speedY;
this.alpha -= 0.01;
}
draw() {
ctx.globalAlpha = this.alpha;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
}
}
const fireworks = [];
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
fireworks.forEach((firework, index) => {
if (firework.particles.length === 0) {
fireworks.splice(index, 1);
} else {
firework.update();
firework.draw();
}
});
requestAnimationFrame(animate);
}
canvas.addEventListener('click', (e) => {
const x = e.clientX;
const y = e.clientY;
const color = `hsl(${Math.random() * 360}, 50%, 50%)`;
fireworks.push(new Firework(x, y, color));
});
animate();
</script>
</body>
</html>
```