利用caves生成烟花效果
整体设计思路是创建一个动态的烟花效果,通过不断的创建和更新粒子来模拟烟花爆炸和消散的过程。使用canvas
进行图形绘制,利用JavaScript的类和对象来管理粒子和烟花的状态,通过requestAnimationFrame
实现流畅的动画效果。
剖析源码
<!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; padding: 0; background: black; overflow: hidden; } canvas { display: block; } </style></head><body><canvas id="fireworksCanvas"></canvas><script> // 获取canvas元素并设置其宽高 const canvas = document.getElementById('fireworksCanvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; // 生成随机数 function random(min, max) { return Math.random() * (max - min) + min; } // 粒子类 class Particle { constructor(x, y, color) { this.x = x; this.y = y; this.color = color; this.radius = random(2, 5);// 粒子的半径,随机生成2到5之间 this.opacity = 1;// 粒子的不透明度 this.velocity = {// 粒子的速度 x: random(-5, 5), y: random(-5, -20) }; } // 绘制粒子的方法 draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false); ctx.fillStyle = `rgba(${this.color.r}, ${this.color.g}, ${this.color.b}, ${this.opacity})`; ctx.fill(); } // 更新粒子状态的方法 update() { this.x += this.velocity.x; this.y += this.velocity.y; this.velocity.y += 0.1; // 模拟重力效果 this.opacity -= 0.01;// 粒子逐渐消失的效果 this.draw(); } } // 烟花类 class Firework { constructor(x, y) { this.particles = []; const colors = ["255,0,0", "0,255,0", "0,0,255", "255,255,0", "0,255,255", "255,0,255"]; for (let i = 0; i < 100; i++) { // 创建粒子并赋予随机颜色 const color = new Color(parseInt(colors[(Math.random() * colors.length) | 0]), 255, 255); this.particles.push(new Particle(x, y, color)); } } // 更新烟花中所有粒子的状态 update() { this.particles.forEach(particle => particle.update()); this.particles = this.particles.filter(particle => particle.opacity > 0); } } // 颜色类 class Color { constructor(r, g, b) { this.r = r; this.g = g; this.b = b; } } // 存储所有烟花的数组 let fireworks = []; // 触发烟花爆炸的方法 function explodeFirework() { const x = random(0, canvas.width); const y = canvas.height; fireworks.push(new Firework(x, y)); } // 动画渲染的方法 function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); fireworks.forEach(firework => firework.update()); fireworks = fireworks.filter(firework => firework.particles.length > 0); if (Math.random() < 0.05) { // 有5%的概率触发新的烟花 explodeFirework(); } // 开始动画循环 requestAnimationFrame(animate); } animate();</script></body></html>
设计思路:
初始化环境: 使用<canvas>
元素作为画布,通过JavaScript来绘制烟花效果。设置画布的宽高为浏览器窗口的宽高,以便烟花效果能够覆盖整个可视区域。 定义辅助函数和类: random(min, max)
:一个简单的辅助函数,用于生成指定范围内的随机数,这在创建烟花效果时用于随机化粒子的属性。Particle
类:代表烟花爆炸后产生的单个粒子。粒子具有位置、颜色、大小、速度和不透明度等属性。Firework
类:代表一个完整的烟花,由多个Particle
实例组成。Color
类:用于存储颜色信息。 创建烟花: 在一个数组fireworks
中存储所有烟花实例。通过explodeFirework
函数在画布的随机位置创建一个新的烟花。 动画循环: 使用requestAnimationFrame
创建一个动画循环,这个函数会在浏览器准备好绘制下一帧时调用提供的回调函数。在每次动画帧中,更新所有烟花的状态,并清除画布上的旧内容。 更新和渲染: 在animate
函数中,遍历fireworks
数组,更新每个烟花中的粒子状态,并绘制到画布上。粒子状态更新包括位置的改变、速度的调整(模拟重力)、不透明度的减少(模拟烟花逐渐消失的效果)。移除不透明的粒子,以减少计算量并避免内存泄漏。 代码详细讲解:
HTML结构:
<canvas id="fireworksCanvas"></canvas>
:定义了一个画布元素,用于绘制烟花效果。 CSS样式:
设置body
和canvas
的样式,确保画布覆盖整个窗口,并且没有滚动条。 JavaScript代码:
a. 初始化:
const canvas = document.getElementById('fireworksCanvas');const ctx = canvas.getContext('2d');canvas.width = window.innerWidth;canvas.height = window.innerHeight;
获取画布元素并设置其宽高。b. 辅助函数:
function random(min, max) { return Math.random() * (max - min) + min;}
生成一个在min
和max
之间的随机数。c. 粒子类:
class Particle { // ...}
包含粒子的属性和方法,用于绘制和更新粒子状态。d. 烟花类:
class Firework { // ...}
包含一个烟花所需的所有粒子,并负责更新它们。e. 颜色类:
class Color { // ...}
用于表示粒子的颜色。f. 动画循环:
function animate() { // ... requestAnimationFrame(animate);}animate();
清除画布、更新烟花状态、检查并触发新的烟花,然后请求下一帧。 接下来分享几个基于以上思路实现的烟花代码:
案例1:
<!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; padding: 0; background: black; overflow: hidden; display: flex; justify-content: center; align-items: center; height: 100vh; } canvas { display: block; } #congrats { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #ff00ff; /* 紫色 */ font-size: 3em; opacity: 0; transition: opacity 2s ease-in-out; pointer-events: none; white-space: nowrap; font-family: 'Arial Black', sans-serif; text-shadow: 0 0 5px #ffffff, 0 0 10px #ffffff, 0 0 20px #ff00ff, 0 0 30px #ff00ff, 0 0 40px #ff00ff, 0 0 50px #ff00ff, 0 0 75px #ff00ff; animation: flash 3s ease-in-out infinite; } @keyframes flash { 0%, 100% { opacity: 0; } 50% { opacity: 1; } } </style></head><body><canvas id="fireworksCanvas"></canvas><div id="congrats">文本</div><script> const canvas = document.getElementById('fireworksCanvas'); const ctx = canvas.getContext('2d'); const congrats = document.getElementById('congrats'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; function random(min, max) { return Math.random() * (max - min) + min; } function randomColor() { return { r: Math.floor(random(0, 255)), g: Math.floor(random(0, 255)), b: Math.floor(random(0, 255)) }; } class Particle { constructor(x, y, color) { this.x = x; this.y = y; this.color = color; this.radius = random(2, 5); this.opacity = 1; this.velocity = { x: random(-5, 5), y: random(-20, -5) }; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false); ctx.fillStyle = `rgba(${this.color.r}, ${this.color.g}, ${this.color.b}, ${this.opacity})`; ctx.fill(); } update() { this.x += this.velocity.x; this.y += this.velocity.y; this.velocity.y += 0.1; // gravity this.opacity -= 0.01; if (this.opacity > 0) { this.draw(); } } } class Firework { constructor(x, y) { this.particles = []; for (let i = 0; i < 100; i++) { const color = randomColor(); this.particles.push(new Particle(x, y, color)); } } update() { this.particles.forEach(particle => particle.update()); this.particles = this.particles.filter(particle => particle.opacity > 0); } } let fireworks = []; function explodeFirework() { const x = random(0, canvas.width); const y = canvas.height; fireworks.push(new Firework(x, y)); displayText("写下你想显示的文本"); } function displayText(text) { congrats.textContent = text; congrats.style.opacity = 1; setTimeout(() => { congrats.style.opacity = 0; }, 2000); } function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); fireworks.forEach(firework => firework.update()); fireworks = fireworks.filter(firework => firework.particles.length > 0); if (Math.random() < 0.05) { explodeFirework(); } requestAnimationFrame(animate); } animate();</script></body></html>
案例2:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>烟花效果思路原型</title><style> body, html { margin: 0; padding: 0; overflow: hidden; background-color: black; } .firework { position: absolute; bottom: 0; width: 5px; height: 5px; background-color: white; border-radius: 50%; } .particle { position: absolute; border-radius: 50%; background-color: white; }</style></head><body><script> function launchFirework() { const firework = document.createElement('div'); firework.classList.add('firework'); firework.style.left = Math.random() * window.innerWidth + 'px'; document.body.appendChild(firework); const height = Math.random() * (window.innerHeight - 100) + 100; const duration = Math.random() * 3000 + 2000; firework.animate([ { transform: 'translateY(0px)' }, { transform: `translateY(-${height}px)` } ], { duration: duration, easing: 'ease-out', fill: 'forwards' }); setTimeout(() => { explodeFirework(firework, height); document.body.removeChild(firework); }, duration); } function explodeFirework(firework, height) { const particlesCount = Math.random() * 50 + 20; for (let i = 0; i < particlesCount; i++) { const particle = document.createElement('div'); particle.classList.add('particle'); particle.style.left = firework.offsetLeft + 'px'; particle.style.bottom = window.innerHeight - height + 'px'; particle.style.width = particle.style.height = Math.random() * 5 + 'px'; document.body.appendChild(particle); const angle = Math.random() * Math.PI * 2; const distance = Math.random() * 100; const duration = Math.random() * 2000 + 1000; particle.animate([ { transform: 'translate(0, 0) scale(1)' }, { transform: `translate(${distance * Math.cos(angle)}px,${distance * Math.sin(angle)}px) scale(0)` } ], { duration: duration, easing: 'ease-out', fill: 'forwards' }); setTimeout(() => { document.body.removeChild(particle); }, duration); } } function randomFireworks() { setInterval(() => { const delay = Math.random() * 1000; setTimeout(launchFirework, delay); }, 500); } randomFireworks();</script></body></html>
案例3:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>烟花效果原型</title><style> body, html { margin: 0; padding: 0; overflow: hidden; } canvas { background: black; }</style></head><body><canvas id="fireworks"></canvas><script>// 获取canvas元素并设置其宽高const canvas = document.getElementById('fireworks');const ctx = canvas.getContext('2d');canvas.width = window.innerWidth;canvas.height = window.innerHeight;// 烟花粒子类class Particle { constructor(x, y, color) { this.x = x; this.y = y; this.color = color; this.velocity = { x: (Math.random() - 0.5) * 5, y: (Math.random() - 0.5) * 5 }; this.alpha = 1; } draw() { ctx.save(); ctx.globalAlpha = this.alpha; ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, 3, 0, Math.PI * 2, false); ctx.fill(); ctx.restore(); } update() { this.velocity.y += 0.1; // 加速度模拟重力 this.x += this.velocity.x; this.y += this.velocity.y; this.alpha -= 0.015; // 粒子渐隐效果 // 检查粒子是否超出屏幕 if (this.x < 0 || this.x > canvas.width || this.y < 0 || this.y > canvas.height) { this.alpha = 0; // 将alpha设置为0来移除粒子 } }}// 烟花类class Firework { constructor() { this.x = Math.random() * canvas.width; this.y = canvas.height; this.color = `hsl(${Math.random() * 360}, 100%, 50%)`; const maxInitialVelocity = -Math.sqrt(2 * 0.1 * canvas.height); // 最大初始速度 // 定义最大高度和最小高度 const maxHeight = 1; const minHeight = 0.4; this.velocity = { x: (Math.random() - 0.5) * 6, y: maxInitialVelocity * (Math.random() * (maxHeight - minHeight) + minHeight) // 随机速度 }; this.particles = []; this.exploded = false; } draw() { if (!this.exploded) { ctx.save(); ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, 4, 0, Math.PI * 2, false); ctx.fill(); ctx.restore(); } this.particles.forEach(particle => particle.draw()); } update() { if (!this.exploded) { this.velocity.y += 0.1; // 加速度模拟重力 this.x += this.velocity.x; this.y += this.velocity.y; // 确保烟花不会超出屏幕 if (this.x < 0 || this.x > canvas.width) { this.velocity.x *= -1; } if (this.velocity.y >= 0) { this.explode(); } } this.particles.forEach((particle, index) => { if (particle.alpha <= 0) { this.particles.splice(index, 1); } else { particle.update(); } }); } explode() { for (let i = 0; i < Math.random() * 10 + 40; i++) { this.particles.push(new Particle(this.x, this.y, this.color)); } this.exploded = true; }}let fireworks = [];function createFirework() { fireworks.push(new Firework());}function animate() { requestAnimationFrame(animate); ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; ctx.fillRect(0, 0, canvas.width, canvas.height); fireworks.forEach((firework, index) => { if (firework.particles.length === 0 && firework.exploded) { fireworks.splice(index, 1); } else { firework.update(); firework.draw(); } });}function launchRandomFireworks() { setTimeout(() => { createFirework(); launchRandomFireworks(); }, Math.random() * 50);}launchRandomFireworks();animate();</script></body>
案例4:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>烟花效果原型2</title><style> body, html { margin: 0; padding: 0; overflow: hidden; } canvas { background: black; }</style></head><body><canvas id="fireworks"></canvas><script>// 获取canvas元素并设置其宽高const canvas = document.getElementById('fireworks');const ctx = canvas.getContext('2d');canvas.width = window.innerWidth;canvas.height = window.innerHeight;// 烟花发射时的粒子类class FireworkParticle { constructor(x, y, color) { this.x = x; this.y = y; this.color = color; this.velocity = { x: (Math.random() - 0.5) * 3, y: -(Math.random() * 8 + 5) // 向上的速度 }; this.alpha = 1; } draw() { ctx.save(); ctx.globalAlpha = this.alpha; ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, 2, 0, Math.PI * 2, false); ctx.fill(); ctx.restore(); } update() { this.velocity.y += 0.05; // 较小的重力影响 this.x += this.velocity.x; this.y += this.velocity.y; this.alpha -= 0.01; // 较快的渐隐效果 if (this.alpha <= 0) { this.alpha = 0; } }}// 烟花爆炸时的粒子类class ExplosionParticle { constructor(x, y, color) { this.x = x; this.y = y; this.color = color; this.velocity = { x: (Math.random() - 0.5) * 6, y: (Math.random() - 0.5) * 6 }; this.alpha = 1; } draw() { ctx.save(); ctx.globalAlpha = this.alpha; ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, 3, 0, Math.PI * 2, false); ctx.fill(); ctx.restore(); } update() { // this.velocity.y += 0.05; // 较小的重力影响 this.x += this.velocity.x; this.y += this.velocity.y; this.alpha -= 0.015; // 渐隐效果 if (this.alpha <= 0) { this.alpha = 0; } }}// 烟花类class Firework { constructor() { this.x = Math.random() * canvas.width; this.y = canvas.height; this.color = `hsl(${Math.random() * 360}, 100%, 50%)`; const maxInitialVelocity = -Math.sqrt(2 * 0.1 * canvas.height); const maxHeight = 1; const minHeight = 0.4; this.velocity = { x: (Math.random() - 0.5) * 6, y: maxInitialVelocity * (Math.random() * (maxHeight - minHeight) + minHeight) // 随机速度 }; this.particles = []; this.explosionParticles = []; this.exploded = false; } draw() { if (!this.exploded) { ctx.save(); ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, 4, 0, Math.PI * 2, false); ctx.fill(); ctx.restore(); } this.particles.forEach(particle => particle.draw()); this.explosionParticles.forEach(particle => particle.draw()); } update() { this.particles.forEach(particle => { particle.update(); if (particle.alpha <= 0) { const index = this.particles.indexOf(particle); if (index > -1) { this.particles.splice(index, 1); } } }); this.explosionParticles.forEach(particle => { particle.update(); if (particle.alpha <= 0) { const index = this.explosionParticles.indexOf(particle); if (index > -1) { this.explosionParticles.splice(index, 1); } } }); if (!this.exploded) { this.velocity.y += 0.1; // 加速度模拟重力 this.x += this.velocity.x; this.y += this.velocity.y; if (this.x < 0 || this.x > canvas.width) { this.velocity.x *= -1; } if (this.velocity.y >= 0) { this.explode(); } } } explode() { for (let i = 0; i < Math.random() * 10 + 40; i++) { this.explosionParticles.push(new ExplosionParticle(this.x, this.y, this.color)); } this.exploded = true; }}let fireworks = [];function createFirework() { fireworks.push(new Firework());}function animate() { requestAnimationFrame(animate); ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; ctx.fillRect(0, 0, canvas.width, canvas.height); fireworks.forEach((firework, index) => { if (firework.particles.length === 0 && firework.explosionParticles.length === 0 && firework.exploded) { fireworks.splice(index, 1); } else { firework.update(); firework.draw(); } });}function launchRandomFireworks() { setTimeout(() => { createFirework(); launchRandomFireworks(); }, Math.random() * 50);}launchRandomFireworks();animate();</script></body></html>
案例5:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>烟花效果原型</title><style> body, html { margin: 0; padding: 0; overflow: hidden; } canvas { background: black; }</style></head><body><canvas id="fireworks"></canvas><script>// 获取canvas元素并设置其宽高const canvas = document.getElementById('fireworks');const ctx = canvas.getContext('2d');canvas.width = window.innerWidth;canvas.height = window.innerHeight;// 烟花发射时的粒子类class FireworkParticle { constructor(x, y, color) { this.x = x; this.y = y; this.color = color; this.velocity = { x: (Math.random() - 0.5) * 3, y: -(Math.random() * 8 + 5) // 向上的速度 }; this.alpha = 1; } draw() { ctx.save(); ctx.globalAlpha = this.alpha; ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, 2, 0, Math.PI * 2, false); ctx.fill(); ctx.restore(); } update() { this.velocity.y += 0.05; // 较小的重力影响 this.x += this.velocity.x; this.y += this.velocity.y; this.alpha -= 0.01; // 较快的渐隐效果 if (this.alpha <= 0) { this.alpha = 0; } }}// 烟花爆炸时的粒子类class ExplosionParticle { constructor(x, y, color, size) { this.x = x; this.y = y; this.color = color; this.velocity = { x: (Math.random() - 0.5) * 6, y: (Math.random() - 0.5) * 6 }; this.alpha = 1; this.size = size; // 粒子大小 } draw() { ctx.save(); ctx.globalAlpha = this.alpha; ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false); ctx.fill(); ctx.restore(); } update() { this.x += this.velocity.x; this.y += this.velocity.y; this.alpha -= 0.015; // 渐隐效果 if (this.alpha <= 0) { this.alpha = 0; } }}// 烟花类class Firework { constructor() { this.x = Math.random() * canvas.width; this.y = canvas.height; this.color = this.getRandomColor(); // 使用随机颜色 const maxInitialVelocity = -Math.sqrt(2 * 0.1 * canvas.height); const maxHeight = 1; const minHeight = 0.4; this.velocity = { x: (Math.random() - 0.5) * 6, y: maxInitialVelocity * (Math.random() * (maxHeight - minHeight) + minHeight) // 随机速度 }; this.particles = []; this.explosionParticles = []; this.exploded = false; } getRandomColor() { const r = Math.floor(Math.random() * 256); const g = Math.floor(Math.random() * 256); const b = Math.floor(Math.random() * 256); return `rgb(${r}, ${g}, ${b})`; } draw() { if (!this.exploded) { ctx.save(); ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, 4, 0, Math.PI * 2, false); ctx.fill(); ctx.restore(); } this.particles.forEach(particle => particle.draw()); this.explosionParticles.forEach(particle => particle.draw()); } update() { this.particles.forEach(particle => { particle.update(); if (particle.alpha <= 0) { const index = this.particles.indexOf(particle); if (index > -1) { this.particles.splice(index, 1); } } }); this.explosionParticles.forEach(particle => { particle.update(); if (particle.alpha <= 0) { const index = this.explosionParticles.indexOf(particle); if (index > -1) { this.explosionParticles.splice(index, 1); } } }); if (!this.exploded) { this.velocity.y += 0.1; // 加速度模拟重力 this.x += this.velocity.x; this.y += this.velocity.y; if (this.x < 0 || this.x > canvas.width) { this.velocity.x *= -1; } if (this.velocity.y >= 0) { this.explode(); } } } explode() { const particleCount = Math.random() * 20 + 30; // 增加粒子数量 for (let i = 0; i < particleCount; i++) { const size = Math.random() * 3 + 1; // 粒子大小变化 const newColor = this.getRandomColor(); // 每个粒子可能有不同的颜色 this.explosionParticles.push(new ExplosionParticle(this.x, this.y, newColor, size)); } this.exploded = true; }}let fireworks = [];function createFirework() { fireworks.push(new Firework());}function animate() { requestAnimationFrame(animate); ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; ctx.fillRect(0, 0, canvas.width, canvas.height); fireworks.forEach((firework, index) => { if (firework.particles.length === 0 && firework.explosionParticles.length === 0 && firework.exploded) { fireworks.splice(index, 1); } else { firework.update(); firework.draw(); } });}function launchRandomFireworks() { setTimeout(() => { createFirework(); launchRandomFireworks(); }, Math.random() * 50);}launchRandomFireworks();animate();</script></body></html>
以上代码是我给我朋友帮忙时写的,但是后来没用上,现分享出来给大家
未完待续。。。文章不定时更新