往年杭州又开始办烟花大会了,那烟花真是丑陋。没有机会现场看的话,作为前端,能够本人实现一个烟花页面。
来跟我一起,把这个烟花代码学废。

性能拆分

常见的烟花都是从一个中央往上收回,到肯定高度后,爆炸成一圈光点。高度有不同,圈的大小有不同,光点的色彩也会有不同。
所以一个烟花的过程能够划分成2步:1. 从某个地位P0回升到P1;2. 从P1爆炸成一圈光点,光点向外扩散,光点扩散到肯定水平,开始慢慢隐没。

逐渐写代码

这个烟花,咱们须要用canvas来画,所以咱们须要先把画布弄好:

var canvas = document.getElementById('canvas');var context = canvas.getContext('2d');// 清空画布function clearCanvas() {  ctx.fillStyle = '#000000';  ctx.fillRect(0, 0, canvas.width, canvas.height);}clearCanvas();

烟花升空

先把烟花的根底信息设置好,包含烟花粒子的大小,开始放烟花的地位。

const size = 2; // 烟花粒子的大小const start = {  x: canvas.width / 2,  y: canvas.height - 100,};

而后做一个鼠标点击事件,当点击canvas时,就会开始播放烟花(fire办法)。在fire办法中,一直的去更新以后烟花粒子的地位,造成一个烟花往上的成果。

let rid = 0;function fire() {  const p = {x: start.x, y: start.y}  function tick() {    clearCanvas();    step1(p);    rid = requestAnimationFrame(tick);  }  cancelAnimationFrame(rid);  tick()}// 烟花第一步,升空function step1(p) {  p.y = p.y - 4; // 每一帧都扭转粒子的地位  ctx.beginPath();  ctx.arc(p.x, p.y, size, 0, Math.PI*2, false)  ctx.closePath();  ctx.fillStyle = "#ffffff";  ctx.fill();}function mouseDownHandler(e) {  fire();}document.addEventListener('mousedown', mouseDownHandler, false);


当烟花回升过程中,速度会越来越慢,当达到肯定高度时,回升速度会到0,并且进入第二阶段。所以p.y的扭转须要改成变动的,而后加一个进入第二状态的管制。

function tick() {  clearCanvas();  // 依据状态管制阶段  if (p.state === 1) {    step1(p);  } else if(p.state === 2) {    step2(p);  }}function step1(p) {  if (p.upSpeed < 0.1) { // 速度很低时,进入第二阶段    p.state = 2;    return;  }  p.upSpeed = p.upSpeed - 0.05; // 速度变动逐渐升高  p.y = p.y - p.upSpeed;  ...}

烟花炸开

进入第二阶段后,须要炸开一堆往周围扩散的粒子。咱们先做出一圈粒子,再减少动画和粒子。

let radius = 10; // 粒子盘绕半径function step2(p) {  const count = 10; // 10个粒子  radius++ // 半径一直变大,造成动画  for (var i = 0; i < count; i++) {    var angle = 360/count * i;    let radians = angle * Math.PI / 180;    let vx = Math.cos(radians) * radius;    let vy = Math.sin(radians) * radius;    ctx.beginPath();    ctx.arc(p.x - 5 + vx, p.y - 5 + vy, size, 0, Math.PI * 2, false);    ctx.closePath();    ctx.fillStyle = '#ffffff';    ctx.fill();  }}

而后当初咱们要减少粒子的个数,这里扭转count就行,每一个粒子并不是均匀散布的,所以angle和radius要随机。有step2是每次刷新都会从新运行的,所以粒子的生产须要独自出一个办法来,不然每次刷新,粒子都要从新随机产生,这样粒子就乱飞了。

// 革新办法,把烟花的创立放到一起。收入同时多个烟花var fireworks = [];function createFireworks(p) {  const firework = {    x: start.x,    y: start.y,    upSpeed: 12,    state: 1,    particles: [], // 炸开后的粒子  };  var count = Math.floor(Math.random() * 100) + 80;  for (var i = 0; i < count; i++) {    var p = {vx:0, vy:0}; // 每一个粒子,vx,vy示意绝对暴涨地位的偏移    var angle = Math.floor(Math.random() * 360);    p.radians = angle * Math.PI / 180;    p.speed = (Math.random() * 5) + .4;    p.radius = p.speed;    p.size = Math.floor(Math.random() * 3) + 1;    firework.particles.push(p);  }}function step2(f) {  const particles = f.particles;  for (let i = 0, len = particles.length; i < len; i++) {    const p = particles[i];    let vx = Math.cos(p.radians) * p.radius;    let vy = Math.sin(p.radians) * p.radius + 0.4;    p.vx += vx;    p.vy += vy;    p.radius *= 1 - p.speed / 100; // 逐渐变慢    ctx.beginPath();    ctx.arc(f.x - 5 + p.vx, f.y - 5 + p.vy, size, 0, Math.PI * 2, false);    ctx.closePath();    ctx.fillStyle = '#ffffff';    ctx.fill();  }}

到这一步,烟花根本样子曾经OK了,前面就是在一段时间后,烟花须要隐没,以及对烟花做不同的款式用于辨别,最好是发射进来的角度也要有不同,起始地位也要有所不同。

烟花色彩不同以及隐没

烟花隐没比较简单,对每一个粒子做一个透明度就行,透明度逐步变小,最终隐没。色彩的话,能够通过hsla来做随机。

function createFireworks() {  const firework = {...};  let count = Math.floor(Math.random() * 100) + 80;  let hue = Math.floor(Math.random() * 6) * 60; // 分6种主色彩  for (let i = 0; i < count; i++) {    ...    p.hue = Math.floor(Math.random() * 30) + hue; // 每一种主色彩上稍微变一变    p.brightness = Math.floor(Math.random() * 30) + 70;    p.alpha = (Math.floor(Math.random() * 61) + 40) / 100;    ...  }}function step2(f) {  for (let i = 0, len = particles.length; i < len; i++) {    ...    p.alpha -= 0.005; // 逐步隐没    ...    ctx.fillStyle = 'hsla(' + p.hue + ', 100%, ' + p.brightness + '%, ' + p.alpha + ')'; // hsla色彩  }}

烟花起始地位和发射角度随机

最初就是把烟花的收回地位和发射角度做肯定的随机。

function createFireworks() {  const firework = {    x: start.x + Math.floor(Math.random() * 100) - 50, // x上的地位随机    ...  };  ...}function step1(p) {  ...  p.x = p.x + p.xAngle; // 回升过程中,x方向的变动  ...}

完满,曾经比得上烟花大会的烟花成果了。最初就是加一些不同类型的烟花,我这里简略弄一个心型的,简单的就靠各位了。

不同类型的烟花

如果要减少一个爱心型的烟花,要怎么做呢,首先须要把爆炸的step2给拆分下,反对多种类型的渲染。

function step2(f) {  const particles = f.particles;  for (let i = 0, len = particles.length; i < len; i++) {    const p = particles[i];    if (p.alpha <= 0) {      continue;    }    // 依据不同的烟花类型,采纳不同的渲染办法    switch(f.type){      case 1:        step2_circle(p, f.x, f.y); // 这个是原先的圆形烟花        break;      case 2:        step2_heart(p, f.x, f.y);        break;      default:        step2_circle(p, f.x, f.y);        break;    }  }}// createFireworks生产的firework增加一个type属性,用于管制不同的烟花类型function createFireworks(type) {  const firework = {    ...    type: Math.floor(Math.random() * 2) + 1,  };}

爱心型

最初就是把爱心的烟花做一个独自的渲染办法:

// 爆炸后的成果是爱心function step2_heart(p, x, y) {  const t = p.radians  let vx = p.radius * Math.pow(Math.sin(t), 3);  let vy = p.radius / 1.2 * Math.cos(t)    - p.radius / 3.2 * Math.cos(2*t)     - p.radius / 8 * Math.cos(3*t)     - p.radius / 16 * Math.cos(4*t)     + p.radius / 6.4;  p.vx += vx;  p.vy -= vy;  p.radius *= 1 - p.speed / 100; // 逐渐变慢  p.alpha -= 0.005;  ctx.beginPath();  ctx.arc(x - 5 + p.vx, y - 5 + p.vy, size, 0, Math.PI * 2, false);  ctx.closePath();  ctx.fillStyle = `hsla(${p.hue}, 100%, ${p.brightness}%, ${p.alpha})`;  ctx.fill();}

最初

最初有一个疑难,如果要实现烟花炸开是一段文字,该怎么做呢?

完结

好了,本文到此结束,心愿本文对你有所帮忙 :-)
最近新弄了一个公众号:写代码的浩,求关注 。前面会逐渐把把握的前端常识以及职场常识积淀下来。
如果还有什么疑难或者倡议,能够多多交换,原创文章,文笔无限,满腹经纶,文中若有不正之处,万望告知。