点击放烟花效果,随便玩玩
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>炫酷烟花效果</title> <style> body { margin: 0; overflow: hidden; background: radial-gradient(circle at center, #000033, #000); } canvas { display: block; } </style> </head> <body> <canvas id="fireworksCanvas"></canvas> <script src="fireworks.js"></script> </body> </html>
JS
const canvas = document.getElementById('fireworksCanvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }); const fireworks = []; const particles = []; class Firework { constructor(x, y, targetX, targetY) { this.x = x; this.y = y; this.targetX = targetX; this.targetY = targetY; this.speed = 3; this.angle = Math.atan2(targetY - y, targetX - x); this.distance = Math.hypot(targetX - x, targetY - y); this.trail = []; this.trailLength = 8; this.done = false; } update() { const moveX = Math.cos(this.angle) * this.speed; const moveY = Math.sin(this.angle) * this.speed; this.trail.push({ x: this.x, y: this.y }); if (this.trail.length > this.trailLength) { this.trail.shift(); } this.x += moveX; this.y += moveY; if (Math.hypot(this.targetX - this.x, this.targetY - this.y) <= this.speed) { this.done = true; explode(this.targetX, this.targetY); } } draw() { ctx.beginPath(); ctx.moveTo(this.trail[0]?.x || this.x, this.trail[0]?.y || this.y); this.trail.forEach(point => ctx.lineTo(point.x, point.y)); ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)'; ctx.lineWidth = 2; ctx.stroke(); ctx.beginPath(); ctx.arc(this.x, this.y, 3, 0, Math.PI * 2); ctx.fillStyle = 'white'; ctx.fill(); } } class Particle { constructor(x, y, color) { this.x = x; this.y = y; this.size = Math.random() * 2 + 1; this.speedX = Math.random() * 5 - 2.5; this.speedY = Math.random() * 5 - 2.5; this.gravity = 0.05; this.friction = 0.98; this.alpha = 1; this.decay = Math.random() * 0.02 + 0.01; this.color = color; } update() { this.speedX *= this.friction; this.speedY *= this.friction; this.speedY += this.gravity; this.x += this.speedX; this.y += this.speedY; this.alpha -= this.decay; if (this.alpha <= 0) { this.alpha = 0; } } draw() { ctx.save(); ctx.globalAlpha = this.alpha; ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); ctx.restore(); } } function explode(x, y) { const colors = ['#FF1461', '#18FF92', '#5A87FF', '#FBF38C', '#FF61A6']; const particleCount = 150; for (let i = 0; i < particleCount; i++) { const color = colors[Math.floor(Math.random() * colors.length)]; particles.push(new Particle(x, y, color)); } } function animate() { ctx.fillStyle = 'rgba(0, 0, 0, 0.15)'; ctx.fillRect(0, 0, canvas.width, canvas.height); fireworks.forEach((firework, index) => { firework.update(); firework.draw(); if (firework.done) { fireworks.splice(index, 1); } }); particles.forEach((particle, index) => { particle.update(); particle.draw(); if (particle.alpha === 0) { particles.splice(index, 1); } }); requestAnimationFrame(animate); } canvas.addEventListener('click', (e) => { const x = canvas.width / 2; const y = canvas.height; const targetX = e.clientX; const targetY = e.clientY; fireworks.push(new Firework(x, y, targetX, targetY)); }); animate();
使用 CSS 渐变背景
烟花尾迹:通过 trail
数组记录烟花移动的历史位置,绘制出渐隐的尾迹效果。