|
|
| class FireworksAnimation extends HTMLElement { |
| connectedCallback() { |
| this.attachShadow({ mode: 'open' }); |
| this.shadowRoot.innerHTML = ` |
| <style> |
| :host { |
| display: block; |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| pointer-events: none; |
| z-index: 9997; |
| } |
| canvas { |
| display: block; |
| background: transparent; |
| } |
| </style> |
| <canvas id="fireworks"></canvas> |
| `; |
|
|
| const canvas = this.shadowRoot.getElementById('fireworks'); |
| const ctx = canvas.getContext('2d'); |
|
|
| function resize() { |
| canvas.width = window.innerWidth; |
| canvas.height = window.innerHeight; |
| } |
| resize(); |
| window.addEventListener('resize', resize); |
|
|
| let rockets = []; |
| let particles = []; |
| let lastLaunch = Date.now(); |
|
|
| class Rocket { |
| constructor() { |
| this.x = Math.random() * canvas.width; |
| this.y = canvas.height; |
| this.vy = -8 - Math.random() * 3; |
| this.exploded = false; |
| this.color = `hsl(${Math.random() * 60 + 0}, 100%, 50%)`; |
| } |
| } |
|
|
| class Particle { |
| constructor(x, y, color) { |
| this.x = x; |
| this.y = y; |
| const angle = Math.random() * Math.PI * 2; |
| const speed = Math.random() * 5 + 3; |
| this.vx = Math.cos(angle) * speed; |
| this.vy = Math.sin(angle) * speed; |
| this.life = 120; |
| this.alpha = 1; |
| this.color = color; |
| } |
|
|
| update() { |
| this.x += this.vx; |
| this.y += this.vy; |
| this.vy += 0.04; |
| this.life--; |
| this.alpha = this.life / 120; |
| } |
|
|
| draw(ctx) { |
| ctx.beginPath(); |
| ctx.arc(this.x, this.y, 2, 0, Math.PI * 2); |
| ctx.fillStyle = `${this.color.replace(')', `, ${this.alpha})`).replace('hsl', 'hsla')}`; |
| ctx.shadowBlur = 10; |
| ctx.shadowColor = this.color; |
| ctx.fill(); |
| } |
| } |
|
|
| function explode(x, y, color) { |
| const count = 180; |
| for (let i = 0; i < count; i++) { |
| const angle = (Math.PI * 2) * (i / count); |
| const speed = Math.random() * 5 + 3; |
| particles.push(new Particle(x, y, color)); |
| } |
| } |
|
|
| function launchRocket() { |
| rockets.push(new Rocket()); |
| lastLaunch = Date.now(); |
| } |
|
|
| function animate() { |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
|
|
| |
| if (Date.now() - lastLaunch > 2000 + Math.random() * 3000) { |
| launchRocket(); |
| } |
|
|
| |
| rockets.forEach((rocket, i) => { |
| rocket.y += rocket.vy; |
|
|
| |
| ctx.beginPath(); |
| ctx.moveTo(rocket.x, rocket.y + 30); |
| ctx.lineTo(rocket.x, rocket.y); |
| ctx.strokeStyle = "rgba(255,255,255,0.8)"; |
| ctx.lineWidth = 2; |
| ctx.stroke(); |
|
|
| |
| if (rocket.y < canvas.height * (0.3 + Math.random() * 0.3)) { |
| explode(rocket.x, rocket.y, rocket.color); |
| rockets.splice(i, 1); |
| } |
| }); |
|
|
| |
| particles.forEach((p, i) => { |
| p.update(); |
| p.draw(ctx); |
| if (p.life <= 0) particles.splice(i, 1); |
| }); |
|
|
| requestAnimationFrame(animate); |
| } |
| |
| setTimeout(launchRocket, 500); |
| animate(); |
| setTimeout(() => { |
| this.remove(); |
| }, 6000); |
| } |
| } |
| customElements.define('fireworks-animation', FireworksAnimation); |