JavaScript实现烟花绽放动画效果


Posted in Javascript onAugust 04, 2020

      先编写一个烟花绽放的动画效果。

      放烟花时,一个烟花可分为两个阶段:(1)烟花上升到空中;(2)烟花炸开成碎片,炸开的碎片慢慢消散。

      为此抽象出两个对象类:Firework和Particle。其中,Firework用于表示一个烟花对象,Particle用于表示一个烟花炸开后的各碎片。

      Firework对象类定义6个属性:表示烟花上升轨迹中各点的坐标(x,y)、烟花弧状轨迹的偏转角度angle、上升阶段水平和垂直方向的位移改变量xSpeed和ySpeed、烟花的色彩色相hue。

      坐标属性值y的初始值取画布的高度,表示烟花从地面上升到空中,其余各属性的初始值采用随机数确定。具体定义如下:

function Firework()

  {

   this.x = canvas.width/4*(1+3*Math.random());

   this.y = canvas.height - 15;

   this.angle = Math.random() * Math.PI / 4 - Math.PI / 6;

   this.xSpeed = Math.sin(this.angle) *(6+Math.random()*7);

   this.ySpeed = -Math.cos(this.angle) *(6+Math.random()*7);

   this.hue = Math.floor(Math.random() * 360);

  }

      Firework对象类定义3个方法:绘制烟花上升轨迹的方法draw()、烟花上升时坐标改变方法update()和烟花炸开方法explode()。绘制烟花轨迹时,在各点(x,y)处绘制一个宽度为5、高度为15的填充小矩形表示一个轨迹点。烟花上升时,垂直方向速度ySpeed初始值为负的,每次上升时,ySpeed加上一个正值,表示上升在减速,当ySpeed的值大于0时,烟花上升到顶了(不能再上升),就炸开为70个碎片。具体方法的实现见后面的HTML文件内容。

       Particle对象类定义8个属性:表示碎片散开轨迹中各点的坐标(x,y)、碎片弧状轨迹的偏转角度angle、散开时水平和垂直方向的位移改变量xSpeed和ySpeed、碎片的色彩色相hue、表示碎片小圆的半径size、碎片的亮度lightness。

function Particle(x,y,hue)

  {

   this.x = x;

   this.y = y;

   this.hue = hue;

   this.lightness = 50;

   this.size = 15 + Math.random() * 10;

   this.angle = Math.random() * 2 * Math.PI;

   this.xSpeed = Math.cos(this.angle) *(1+Math.random() * 6);

   this.ySpeed = Math.sin(this.angle) *(1+Math.random() * 6);

  }

       Particle对象类定义2个方法:绘制碎片散开轨迹的方法draw()、碎片散开时坐标改变方法update()。碎片散开时逐渐变小(属性size值减量),当size值小于1时,从碎片数组中删除该碎片,表示碎片已消亡。

       定义两个数组var fireworks=[];和var particles=[];分别存储烟花对象和炸开的碎片对象。

       模拟动画的函数loop中,每隔一段时间(用count计数来实现)向fireworks数组中添加一个烟花对象,烟花对象上升到顶炸开后,从fireworks数组中删除该对象元素,然后向particles数组中添加70个碎片对象。

      遍历两个数组的各对象,分别调用它们的draw()和update()方法。

编写的完整HTML文件内容如下。

<html> 
<head> 
<title>烟花绽放</title> 
</head>
<body>
<canvas id="myCanvas" width="800" height="600" style="border:3px double #996633;background:black;">
</canvas>
<script type="text/javascript">
  var canvas=document.getElementById('myCanvas');
  ctx= canvas.getContext('2d');
  var fireworks=[];
  var particles=[];
  var counter = 0;

  function Firework()
  {
   this.x = canvas.width/4*(1+3*Math.random());
   this.y = canvas.height - 15;
   this.angle = Math.random() * Math.PI / 4 - Math.PI / 6;
   this.xSpeed = Math.sin(this.angle) *(6+Math.random()*7);
   this.ySpeed = -Math.cos(this.angle) *(6+Math.random()*7);
   this.hue = Math.floor(Math.random() * 360);
  }
  Firework.prototype.draw= function() 
  {
   ctx.save();
   ctx.translate(this.x, this.y);
   ctx.rotate(Math.atan2(this.ySpeed, this.xSpeed) + Math.PI / 2);
   ctx.fillStyle =`hsl(${this.hue}, 100%, 50%)`;
   ctx.fillRect(0, 0, 5, 15);
   ctx.restore();
  }
  Firework.prototype.update= function() 
  {
   this.x = this.x + this.xSpeed;
   this.y = this.y + this.ySpeed;
   this.ySpeed += 0.1;
  }
  Firework.prototype.explode= function() 
  {
    for (var i = 0; i < 70; i++) 
    {
     particles.push(new Particle(this.x, this.y, this.hue));
    }
  }

  function Particle(x,y,hue) 
  {
   this.x = x;
   this.y = y;
   this.hue = hue;
   this.lightness = 50;
   this.size = 15 + Math.random() * 10;
   this.angle = Math.random() * 2 * Math.PI;
   this.xSpeed = Math.cos(this.angle) *(1+Math.random() * 6);
   this.ySpeed = Math.sin(this.angle) *(1+Math.random() * 6);
  }
  Particle.prototype.draw= function() 
  {
    ctx.fillStyle = `hsl(${this.hue}, 100%, ${this.lightness}%)`;
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
    ctx.closePath();
    ctx.fill();
  }
  Particle.prototype.update= function(index) 
  {
    this.ySpeed += 0.05;
    this.size = this.size*0.95;
    this.x = this.x + this.xSpeed;
    this.y = this.y + this.ySpeed;
    if (this.size<1) 
    {
      particles.splice(index,1);
    }
  }
  function loop() 
  {
   ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
   ctx.fillRect(0,0,canvas.width,canvas.height);
   counter++;
   if (counter==15) 
   {
     fireworks.push(new Firework());
     counter=0;
   }
   var i=fireworks.length;
   while (i--) 
   {
     fireworks[i].draw();
     fireworks[i].update();
     if (fireworks[i].ySpeed > 0) 
     {
       fireworks[i].explode();
       fireworks.splice(i, 1);
     }
   }
   var i=particles.length;
   while (i--) 
   {   
     particles[i].draw();
     particles[i].update(i);
   }
   requestAnimationFrame(loop);
  }
 loop();
</script>
</body> 
</html>

      在浏览器中打开包含这段HTML代码的html文件,可以看到在浏览器窗口中呈现出如图所示的烟花绽放动画效果。

JavaScript实现烟花绽放动画效果

      实现了烟花绽放的效果,我们还可以继续让一定区域内的绽放的烟花碎片拼成“Happy New Year”粒子文本。

      编写如下的HTML代码。

<html> 
<head> 
<title>迎新年烟花绽放</title> 
<style>
 body { margin: 0; background: black; }
 canvas { position: absolute; }
</style>
</head>
<body>
<canvas id="myCanvas1"></canvas>
<canvas id="myCanvas2"></canvas>
<canvas id="myCanvas3"></canvas>
<script type="text/javascript">
  function Particle(x, y, hue)
  {
   this.x = x;
   this.y = y;
   this.hue = hue;
   this.lightness = 50;
   this.size = 15 + Math.random() * 10;
   this.angle = Math.random() * 2 * Math.PI;
   this.xSpeed = Math.cos(this.angle) * (1 + Math.random() * 6);
   this.ySpeed = Math.sin(this.angle) * (1 + Math.random() * 6);
   this.target = getTarget();
   this.timer = 0;
  }
  Particle.prototype.draw= function() 
  {
   ctx2.fillStyle =`hsl(${this.hue}, 100%, ${this.lightness}%)`;
   ctx2.beginPath();
   ctx2.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
   ctx2.closePath();
   ctx2.fill();
  }
  Particle.prototype.update= function(idx) 
  {
   if (this.target) 
   {
     var dx = this.target.x - this.x;
     var dy = this.target.y - this.y;
     var dist = Math.sqrt(dx * dx + dy * dy);
     var a = Math.atan2(dy, dx);
     var tx = Math.cos(a) * 5;
     var ty = Math.sin(a) * 5;
     this.size = lerp(this.size, 1.5, 0.05);
     if (dist < 5) 
     {
       this.lightness = lerp(this.lightness, 100, 0.01);
       this.xSpeed = this.ySpeed = 0;
       this.x = lerp(this.x, this.target.x + fidelity / 2, 0.05);
       this.y = lerp(this.y, this.target.y + fidelity / 2, 0.05);
       this.timer += 1;
     }
     else if (dist < 10) 
     {
       this.lightness = lerp(this.lightness, 100, 0.01);
       this.xSpeed = lerp(this.xSpeed, tx, 0.1);
       this.ySpeed = lerp(this.ySpeed, ty, 0.1);
       this.timer += 1;
     } 
     else
     {
       this.xSpeed = lerp(this.xSpeed, tx, 0.02);
       this.ySpeed = lerp(this.ySpeed, ty, 0.02);
     }
   } 
   else
   {
     this.ySpeed += 0.05;
     this.size = this.size*0.95;
     if (this.size<1) 
     {
       particles.splice(idx,1);
     }
   }
   this.x = this.x + this.xSpeed;
   this.y = this.y + this.ySpeed;
  }

  function Firework() 
  {
   this.x = canvas2.width*(1+ 3*Math.random())/4;
   this.y = canvas2.height - 15;
   this.angle = Math.random() * Math.PI / 4 - Math.PI / 6;
   this.xSpeed = Math.sin(this.angle) * (6 + Math.random() * 7);
   this.ySpeed = -Math.cos(this.angle) * (6 + Math.random() * 7);
   this.hue = Math.floor(Math.random() * 360);
  }
  Firework.prototype.draw= function() 
  {
   ctx2.save();
   ctx2.translate(this.x, this.y);
   ctx2.rotate(Math.atan2(this.ySpeed, this.xSpeed) + Math.PI / 2);
   ctx2.fillStyle = `hsl(${this.hue}, 100%, 50%)`;
   ctx2.fillRect(0, 0, 5, 15);
   ctx2.restore();
  }
  Firework.prototype.update= function() 
  {
   this.x = this.x + this.xSpeed;
   this.y = this.y + this.ySpeed;
   this.ySpeed += 0.1;
  }
  Firework.prototype.explode= function() 
  {
   for (var i = 0; i < 70; i++) 
   {
     particles.push(new Particle(this.x, this.y, this.hue));
   }
  }

  function lerp(a, b, t)
  {
    return Math.abs(b - a)> 0.1 ? a + t * (b - a) : b;
  }
  function getTarget() 
  {
    if (targets.length > 0) 
    {
      var idx = Math.floor(Math.random() * targets.length);
      var { x, y } = targets[idx];
      targets.splice(idx, 1);
      x += canvas2.width / 2 - textWidth / 2;
      y += canvas2.height / 2 - fontSize / 2;
      return { x, y };
    }
  }

  var canvas1=document.getElementById('myCanvas1');
  ctx1= canvas1.getContext('2d');
  var canvas2=document.getElementById('myCanvas2');
  ctx2= canvas2.getContext('2d');
  var canvas3=document.getElementById('myCanvas3');
  ctx3= canvas3.getContext('2d');
  var fontSize = 200;
  var fireworks = [];
  var particles = [];
  var targets = [];
  var fidelity = 3;
  var counter = 0;
  canvas2.width = canvas3.width = window.innerWidth;
  canvas2.height = canvas3.height = window.innerHeight;
  ctx1.fillStyle = '#000';
  var text = 'Happy New Year';
  var textWidth = 999999;
  while (textWidth > window.innerWidth) 
  {
   ctx1.font = `900 ${fontSize--}px Arial`;
   textWidth = ctx1.measureText(text).width;
  }
  canvas1.width = textWidth;
  canvas1.height = fontSize * 1.5;
  ctx1.font = `900 ${fontSize}px Arial`;
  ctx1.fillText(text, 0, fontSize);
  var imgData = ctx1.getImageData(0, 0, canvas1.width, canvas1.height);
  for (var i = 0, max = imgData.data.length; i < max; i += 4) 
  {
    var alpha = imgData.data[i + 3];
    var x = Math.floor(i / 4) % imgData.width;
    var y = Math.floor(i / 4 / imgData.width);
    if (alpha && x % fidelity === 0 && y % fidelity === 0) 
    {
      targets.push({ x, y });
    }
  }
  ctx3.fillStyle = '#FFF';
  ctx3.shadowColor = '#FFF';
  ctx3.shadowBlur = 25;

  function loop() 
  {
   ctx2.fillStyle = "rgba(0, 0, 0, .1)";
   ctx2.fillRect(0, 0, canvas2.width, canvas2.height);
   counter += 1;
   if (counter==15) 
   {
     fireworks.push(new Firework());
     counter=0;
   }
   var i=fireworks.length;
   while (i--) 
   {
     fireworks[i].draw();
     fireworks[i].update();
     if (fireworks[i].ySpeed > 0) 
     {
       fireworks[i].explode();
       fireworks.splice(i, 1);
     }
   }
   var i=particles.length;
   while (i--) 
   {   
     particles[i].draw();
     particles[i].update(i);
     if (particles[i].timer >= 100 || particles[i].lightness >= 99) 
     {
       ctx3.fillRect(particles[i].target.x, particles[i].target.y, fidelity + 1, fidelity + 1);
       particles.splice(i, 1);
     }
   }
   requestAnimationFrame(loop);
  }
  loop();
</script>
</body> 
</html>

      在浏览器中打开包含这段HTML代码的html文件,可以看到在浏览器窗口中呈现出如图所示的烟花绽放迎新年动画效果。图2中为了控制图片的大小,删除了大量的中间帧,因此和实际运行的效果有所不同。

JavaScript实现烟花绽放动画效果

以上就是JavaScript实现烟花绽放动画效果的详细内容,更多关于JavaScript动画效果的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
一直复略了的一个问题,关于表单重复提交
Feb 15 Javascript
在网页中使用document.write时遭遇的奇怪问题
Aug 24 Javascript
js汉字排序问题 支持中英文混排,兼容各浏览器,包括CHROME
Dec 20 Javascript
jquery实现智能感知连接外网搜索
May 21 Javascript
js判断undefined类型,undefined,null, 的区别详细解析
Dec 16 Javascript
推荐一款jQuery插件模板
Jan 09 Javascript
js实现星星打分效果的方法
Jul 05 Javascript
JS实现六位字符密码输入器功能
Aug 19 Javascript
React Js 微信禁止复制链接分享禁止隐藏右上角菜单功能
May 26 Javascript
vue的基本用法与常见指令
Aug 15 Javascript
详解如何运行vue项目
Apr 15 Javascript
vue 使用axios 数据请求第三方插件的使用教程详解
Jul 05 Javascript
JS事件循环机制event loop宏任务微任务原理解析
Aug 04 #Javascript
解决vue addRoutes不生效问题
Aug 04 #Javascript
vue 解决addRoutes多次添加路由重复的操作
Aug 04 #Javascript
Vue优化:常见会导致内存泄漏问题及优化详解
Aug 04 #Javascript
Jquery cookie插件实现原理代码解析
Aug 04 #jQuery
解决vue自定义指令导致的内存泄漏问题
Aug 04 #Javascript
vue中的v-model原理,与组件自定义v-model详解
Aug 04 #Javascript
You might like
PHP中使用hidef扩展代替define提高性能
2015/04/09 PHP
如何通过View::first使用Laravel Blade的动态模板详解
2017/09/21 PHP
PHP封装的验证码工具类定义与用法示例
2018/08/22 PHP
php curl优化下载微信头像的方法总结
2018/09/07 PHP
因str_replace导致的注入问题总结
2019/08/08 PHP
说说掌握JavaScript语言的思想前提想学习js的朋友可以看看
2009/04/01 Javascript
JQuery实现的在新窗口打开链接的方法小结
2010/04/22 Javascript
jquery实现固定顶部导航效果(仿蘑菇街)
2013/03/21 Javascript
jquery和js实现对div的隐藏和显示方法
2014/09/26 Javascript
实例讲解避免javascript冲突的方法
2016/01/03 Javascript
Node.js中Request模块处理HTTP协议请求的基本使用教程
2016/03/31 Javascript
javascript RegExp 使用说明
2016/05/21 Javascript
AngularJS 过滤器的简单实例
2016/07/27 Javascript
Angularjs中$http以post请求通过消息体传递参数的实现方法
2016/08/05 Javascript
浅谈Javascript事件对象
2017/02/05 Javascript
JS实现图片手风琴效果
2020/04/17 Javascript
javascript性能优化之分时函数的介绍
2018/03/28 Javascript
微信小程序中进行地图导航功能的实现方法
2018/06/29 Javascript
vue 搭建后台系统模块化开发详解
2019/05/01 Javascript
原生JS实现弹幕效果的简单操作指南
2020/11/10 Javascript
js删除对象中的某一个字段的方法实现
2021/01/11 Javascript
[03:03]2014DOTA2国际邀请赛 EG战队专访
2014/07/12 DOTA
用pywin32实现windows模拟鼠标及键盘动作
2014/04/22 Python
python中的break、continue、exit()、pass全面解析
2017/08/05 Python
python定向爬取淘宝商品价格
2018/02/27 Python
Jupyter Notebook远程登录及密码设置操作
2020/04/10 Python
css3中的calc函数浅析
2018/07/10 HTML / CSS
Richards网上商店:当代时尚,遍布巴西
2019/11/03 全球购物
美国购买舞会礼服网站:Couture Candy
2019/12/29 全球购物
感恩的演讲稿
2014/05/06 职场文书
班级文化建设标语
2014/06/23 职场文书
初二数学教学反思
2016/02/17 职场文书
JavaScript canvas实现流星特效
2021/05/20 Javascript
javascript实现计算器功能详解流程
2021/11/01 Javascript
python turtle绘图命令及案例
2021/11/23 Python
OpenStack虚拟机快照和增量备份实现方法
2022/04/04 Servers