利用caves生成烟花效果
整体设计思路是创建一个动态的烟花效果,通过不断的创建和更新粒子来模拟烟花爆炸和消散的过程。使用
canvas
进行图形绘制,利用JavaScript的类和对象来管理粒子和烟花的状态,通过
requestAnimationFrame
实现流畅的动画效果。
剖析源码
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="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><canvasid="fireworksCanvas"></canvas><script>// 获取canvas元素并设置其宽高const canvas = document.getElementById('fireworksCanvas');const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;// 生成随机数functionrandom(min, max){return Math.random()*(max - min)+ min;}// 粒子类classParticle{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();}}// 烟花类classFirework{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 =newColor(parseInt(colors[(Math.random()* colors.length)|0]),255,255);this.particles.push(newParticle(x, y, color));}}// 更新烟花中所有粒子的状态update(){this.particles.forEach(particle=> particle.update());this.particles =this.particles.filter(particle=> particle.opacity >0);}}// 颜色类classColor{constructor(r, g, b){this.r = r;this.g = g;this.b = b;}}// 存储所有烟花的数组let fireworks =[];// 触发烟花爆炸的方法functionexplodeFirework(){const x =random(0, canvas.width);const y = canvas.height;
fireworks.push(newFirework(x, y));}// 动画渲染的方法functionanimate(){
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. 辅助函数:functionrandom(min, max){return Math.random()*(max - min)+ min;}
- 生成一个在min
和max
之间的随机数。 c. 粒子类:classParticle{// ...}
- 包含粒子的属性和方法,用于绘制和更新粒子状态。 d. 烟花类:classFirework{// ...}
- 包含一个烟花所需的所有粒子,并负责更新它们。 e. 颜色类:classColor{// ...}
- 用于表示粒子的颜色。 f. 动画循环:functionanimate(){// ...requestAnimationFrame(animate);}animate();
- 清除画布、更新烟花状态、检查并触发新的烟花,然后请求下一帧。
接下来分享几个基于以上思路实现的烟花代码:
案例1:
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="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><canvasid="fireworksCanvas"></canvas><divid="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;functionrandom(min, max){return Math.random()*(max - min)+ min;}functionrandomColor(){return{r: Math.floor(random(0,255)),g: Math.floor(random(0,255)),b: Math.floor(random(0,255))};}classParticle{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;// gravitythis.opacity -=0.01;if(this.opacity >0){this.draw();}}}classFirework{constructor(x, y){this.particles =[];for(let i =0; i <100; i++){const color =randomColor();this.particles.push(newParticle(x, y, color));}}update(){this.particles.forEach(particle=> particle.update());this.particles =this.particles.filter(particle=> particle.opacity >0);}}let fireworks =[];functionexplodeFirework(){const x =random(0, canvas.width);const y = canvas.height;
fireworks.push(newFirework(x, y));displayText("写下你想显示的文本");}functiondisplayText(text){
congrats.textContent = text;
congrats.style.opacity =1;setTimeout(()=>{
congrats.style.opacity =0;},2000);}functionanimate(){
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:
<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="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>functionlaunchFirework(){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);}functionexplodeFirework(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);}}functionrandomFireworks(){setInterval(()=>{const delay = Math.random()*1000;setTimeout(launchFirework, delay);},500);}randomFireworks();</script></body></html>
案例3:
<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>烟花效果原型</title><style>body, html{margin: 0;padding: 0;overflow: hidden;}canvas{background: black;}</style></head><body><canvasid="fireworks"></canvas><script>// 获取canvas元素并设置其宽高const canvas = document.getElementById('fireworks');const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;// 烟花粒子类classParticle{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来移除粒子}}}// 烟花类classFirework{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(newParticle(this.x,this.y,this.color));}this.exploded =true;}}let fireworks =[];functioncreateFirework(){
fireworks.push(newFirework());}functionanimate(){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();}});}functionlaunchRandomFireworks(){setTimeout(()=>{createFirework();launchRandomFireworks();}, Math.random()*50);}launchRandomFireworks();animate();</script></body>
案例4:
<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>烟花效果原型2</title><style>body, html{margin: 0;padding: 0;overflow: hidden;}canvas{background: black;}</style></head><body><canvasid="fireworks"></canvas><script>// 获取canvas元素并设置其宽高const canvas = document.getElementById('fireworks');const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;// 烟花发射时的粒子类classFireworkParticle{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;}}}// 烟花爆炸时的粒子类classExplosionParticle{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;}}}// 烟花类classFirework{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(newExplosionParticle(this.x,this.y,this.color));}this.exploded =true;}}let fireworks =[];functioncreateFirework(){
fireworks.push(newFirework());}functionanimate(){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();}});}functionlaunchRandomFireworks(){setTimeout(()=>{createFirework();launchRandomFireworks();}, Math.random()*50);}launchRandomFireworks();animate();</script></body></html>
案例5:
<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>烟花效果原型</title><style>body, html{margin: 0;padding: 0;overflow: hidden;}canvas{background: black;}</style></head><body><canvasid="fireworks"></canvas><script>// 获取canvas元素并设置其宽高const canvas = document.getElementById('fireworks');const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;// 烟花发射时的粒子类classFireworkParticle{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;}}}// 烟花爆炸时的粒子类classExplosionParticle{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;}}}// 烟花类classFirework{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(newExplosionParticle(this.x,this.y, newColor, size));}this.exploded =true;}}let fireworks =[];functioncreateFirework(){
fireworks.push(newFirework());}functionanimate(){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();}});}functionlaunchRandomFireworks(){setTimeout(()=>{createFirework();launchRandomFireworks();}, Math.random()*50);}launchRandomFireworks();animate();</script></body></html>
以上代码是我给我朋友帮忙时写的,但是后来没用上,现分享出来给大家
未完待续。。。文章不定时更新
本文转载自: https://blog.csdn.net/weixin_74905854/article/details/142443212
版权归原作者 韩梓 所有, 如有侵权,请联系我们删除。
版权归原作者 韩梓 所有, 如有侵权,请联系我们删除。