Spaces:
Running
Running
| class Fireworks { | |
| constructor(canvas) { | |
| this.canvas = canvas; | |
| this.ctx = canvas.getContext('2d'); | |
| this.particles = []; | |
| this.rockets = []; | |
| this.isActive = false; | |
| this.resize(); | |
| window.addEventListener('resize', () => this.resize()); | |
| } | |
| resize() { | |
| this.canvas.width = window.innerWidth; | |
| this.canvas.height = window.innerHeight; | |
| } | |
| createRocket() { | |
| this.rockets.push({ | |
| x: Math.random() * this.canvas.width, | |
| y: this.canvas.height, | |
| vx: (Math.random() - 0.5) * 2, | |
| vy: -Math.random() * 3 - 12, | |
| trail: [] | |
| }); | |
| } | |
| createExplosion(x, y) { | |
| const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#f0932b', '#eb4d4b', '#6ab04c', '#130f40']; | |
| const color = colors[Math.floor(Math.random() * colors.length)]; | |
| const particleCount = 50 + Math.random() * 50; | |
| for (let i = 0; i < particleCount; i++) { | |
| const angle = (Math.PI * 2 * i) / particleCount; | |
| const velocity = 2 + Math.random() * 4; | |
| this.particles.push({ | |
| x: x, | |
| y: y, | |
| vx: Math.cos(angle) * velocity, | |
| vy: Math.sin(angle) * velocity, | |
| alpha: 1, | |
| color: color, | |
| size: 2 + Math.random() * 2 | |
| }); | |
| } | |
| } | |
| update() { | |
| // Update rockets | |
| for (let i = this.rockets.length - 1; i >= 0; i--) { | |
| const rocket = this.rockets[i]; | |
| rocket.trail.push({ x: rocket.x, y: rocket.y }); | |
| if (rocket.trail.length > 10) rocket.trail.shift(); | |
| rocket.x += rocket.vx; | |
| rocket.y += rocket.vy; | |
| rocket.vy += 0.1; | |
| if (rocket.vy >= 0) { | |
| this.createExplosion(rocket.x, rocket.y); | |
| this.rockets.splice(i, 1); | |
| } | |
| } | |
| // Update particles | |
| for (let i = this.particles.length - 1; i >= 0; i--) { | |
| const p = this.particles[i]; | |
| p.x += p.vx; | |
| p.y += p.vy; | |
| p.vy += 0.05; | |
| p.vx *= 0.99; | |
| p.alpha -= 0.01; | |
| if (p.alpha <= 0) { | |
| this.particles.splice(i, 1); | |
| } | |
| } | |
| } | |
| draw() { | |
| this.ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; | |
| this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); | |
| // Draw rockets | |
| this.rockets.forEach(rocket => { | |
| this.ctx.beginPath(); | |
| rocket.trail.forEach((point, index) => { | |
| if (index === 0) { | |
| this.ctx.moveTo(point.x, point.y); | |
| } else { | |
| this.ctx.lineTo(point.x, point.y); | |
| } | |
| }); | |
| this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)'; | |
| this.ctx.lineWidth = 2; | |
| this.ctx.stroke(); | |
| }); | |
| // Draw particles | |
| this.particles.forEach(p => { | |
| this.ctx.globalAlpha = p.alpha; | |
| this.ctx.fillStyle = p.color; | |
| this.ctx.beginPath(); | |
| this.ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); | |
| this.ctx.fill(); | |
| // Add glow effect | |
| this.ctx.shadowBlur = 10; | |
| this.ctx.shadowColor = p.color; | |
| this.ctx.fill(); | |
| this.ctx.shadowBlur = 0; | |
| }); | |
| this.ctx.globalAlpha = 1; | |
| } | |
| animate() { | |
| if (!this.isActive) return; | |
| this.update(); | |
| this.draw(); | |
| if (Math.random() < 0.05) { | |
| this.createRocket(); | |
| } | |
| requestAnimationFrame(() => this.animate()); | |
| } | |
| start() { | |
| this.isActive = true; | |
| this.particles = []; | |
| this.rockets = []; | |
| this.animate(); | |
| // Stop after 3 seconds | |
| setTimeout(() => { | |
| this.stop(); | |
| }, 3000); | |
| } | |
| stop() { | |
| this.isActive = false; | |
| setTimeout(() => { | |
| this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| this.particles = []; | |
| this.rockets = []; | |
| }, 1000); | |
| } | |
| } |