烟花效果的算法以及实现

之前在石川县烟花大会的那篇文章中提到过使用js制作出烟花效果,那篇文章中是使用图片的切换来实现的。网速不够快的话效果会非常不给力。而在html5中,可以使用canvas可以进行绘图,所有的效果都不用下载,在客户端就可以实现,所以就不会受到网速的影响了。
那么,如何使用js制作动画呢?以前的话,可以使用setInterval这个函数去实现,现在又出现一个requestAnimationFrame,据说流畅性比setInterval好一些。

canvas的几个绘图的方法可以从这里查看。而requestAnimationFrame这个函数,因为不同的浏览器对象名称不一样,需要考虑一下浏览器兼容性的问题。下面的这段代码就可以保证大部分的浏览器的兼容,可以如果不兼容requestAnimationFrame的话就会选择setTimeout来实现同样的功能。

  requestAnimationFrame = window.requestAnimationFrame
   || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame
   || window.msRequestAnimationFrame || window.oRequestAnimationFrame
   || function(callback) {
   setTimeout(callback, 1000 / 60);
  };


有了canvas跟requestAnimationFrame,就可以轻松实现烟花效果了。下面是具体的算法问题。

首先,模拟一个自由落体运动。

  //requestAnimationFrame
  (function (w, r) {
	  w['r'+r] = w['r'+r] || w['webkitR'+r] || w['mozR'+r] || w['msR'+r] || w['oR'+r] || 
                     function(c){ w.setTimeout(c, 1000 / 60); };
  })(window, 'equestAnimationFrame');
  
  //获取canvas
  var c=document.getElementById("myCanvas");
  var cxt=c.getContext("2d");
  //设置填充背景色为黑色
  cxt.fillStyle="#000000";
  //绘制canvas黑色背景
  cxt.beginPath();
  cxt.fillRect(0, 0, 200, 200);
  cxt.fill();
  
  //初始点坐标 
  var x = 0;  
  var y = 70;
  //初始速度为5,与X轴正方向逆时针呈60度
  var speed = 5;
  var angle = Math.PI * (5/3);
  //x轴 y轴的位移增量
  var incre_x = Math.cos(angle) * speed;
  var incre_y = Math.sin(angle) * speed;
  //重力,因为每秒要绘制60帧,所以这个值的由来是 9.8/60
  var gravity = 0.16;
  
  (function animate(){
	  //计算移动后的坐标,y轴方向需要考虑重力,x轴方向没有加速度
	  incre_y = incre_y +  gravity;		
	  x += incre_x;
	  y += incre_y;
	  //超出画布范围就结束动画
	  if(x > 200 || y > 200){
		  try { window.parent.endAnimation(location.href); } catch (e) {};
		  return;
	  }
	  //开始绘制
	  cxt.fillStyle="#ffffff";
	  cxt.beginPath();
	  cxt.arc(x, y, 2, 0, Math.PI*2, true);
	  cxt.fill();
          //继续播放下一帧
	  requestAnimationFrame(animate);
  })();

接下来就是烟花效果的具体实现部分。烟花燃放一般分几个过程:

  • 由下而上运动到顶端
  • 闪烁着爆炸开来
  • 光亮逐渐消失

由下而上运动的部分这里不做了。只实现从爆炸以后颗粒做自由落体到消失的部分。自由落体在上面已经实现了,但是上面是做出来一个运动轨迹,我们要做动画效果的话需要将前面绘制的擦除掉,实现的方法就是每一帧都绘制一遍画布,这样前面的内容就被遮住,新绘制的内容就会显示出来。但是,这里我没有完全擦除,而是逐渐擦除(比如每次叠加一个透明度20%的层,5次叠加以后就会擦除),这样做会出现一种逐渐消失的效果。

上面模拟了一个烟花颗粒的运动轨迹,但是烟花爆炸的时候有很多颗粒,而且速度方向不一样(这里速度大小的差异就忽略不计了),可以使用随机函数生成其余颗粒的初速度方向。实现烟花闪烁的效果,让邻接的两帧绘制不同的颜色的点就可以实现了。比如第一帧使用黄色,第二帧使用灰色,第三帧再恢复黄色。

光亮逐渐消失部分可以使用一个衰减参数。不仅可以模拟光亮从大变小的效果,在颗粒的移动过程中也可以使用,因为烟花的爆炸颗粒的速度受到的空气阻力的影响还是比较大的。

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

Close Bitnami banner
Bitnami