博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
程序员的小浪漫----烟火
阅读量:6670 次
发布时间:2019-06-25

本文共 6300 字,大约阅读时间需要 21 分钟。

多代码,慎读!!!

预览

完整项目预览----;

属性设计

烟花状态:烟花应有三个状态:

  1. 升空
  2. 等待炸裂
  3. 炸裂后

烟花:发射点(x, y),爆炸点(xEnd, yEnd),升空后等待炸裂时间(wait),炸裂后微粒个数(count),烟花半径(radius)

烟花炸裂后微粒:自身位置(x, y),自身大小(size),自身速度(rate),最大烟花半径(radius)。

config:为全局变量,以及控制参数,包括画布宽高,设定烟花属性等。

设定全局变量

const config = {    width: 360,    height: 600,    canvases: ['bg', 'firework'],    skyColor: '210, 60%, 5%, 0.2)',    fireworkTime:{
min:30,max:60}, //烟花参数本身有默认值 传入undefined则使用默认参数 fireworkOpt:{ x: undefined, y: undefined, xEnd: undefined, yEnd: undefined, count: 300, //炸裂后粒子数 wait: undefined, //消失后 => 炸裂 等待时间 }}复制代码

构建微粒类

class Particle{    //默认值写法     constructor({x, y, size = 1, radius = 1.2} = {}){    	this.x = x;    	this.y = y;    	this.size = size;            	this.rate = Math.random(); //每个微粒移动的速度都是随机不同的    	this.angle = Math.PI * 2 * Math.random(); //每个微粒的偏移角度    	    	//每次微粒移动速度分解为横纵坐标的移动。    	this.vx = radius * Math.cos(this.angle) * this.rate;     	this.vy = radius * Math.sin(this.angle) * this.rate;    }    go(){    	this.x += this.vx;    	this.y += this.vy;     	this.vy += 0.02; //重力影响 y越大实际越偏下    	    	//空气阻力    	this.vx *= 0.98;    	this.vy *= 0.98;    }        //画出微粒的位置    render(ctx){    	this.go();    	ctx.beginPath();    	ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false);    	ctx.fill();    }}复制代码

构建烟花类

class Firework{    constructor({x, y = config.height, xEnd, yEnd, count = 300, wait} = {}){        //烟花自身属性    	this.x = x || config.width / 8 + Math.random() * config.width * 3 / 4;     	this.y = y;    	this.xEnd = xEnd || this.x;    	this.yEnd = yEnd || config.width / 8 + Math.random() * config.width * 3 / 8;    	this.size = 2;    	this.velocity = -3;    	    	this.opacity = 0.8;    	this.color = `hsla(${
360 * Math.random() | 0},80%,60%,1)`; this.wait = wait || 30 + Math.random() * 30; //微粒个数等 this.count = count; this.particles = []; this.createParticles(); this.status = 1; } //创建微粒 createParticles(){ for(let i = 0;i < this.count; ++i){ this.particles.push(new Particle({
x:this.xEnd, y:this.yEnd})); } } //升空 rise(){ this.y += this.velocity * 1; this.velocity += 0.005; //升空时产生的阻力 //烟花升空到目标位置开始渐隐 if(this.y - this.yEnd <= 50){ this.opacity = (this.y - this.yEnd) / 50; } //如果到了目标位置 就开始第二个状态 if(this.y <= this.yEnd){ this.status = 2; } } //渲染烟花 烟花所有动作完成之后返回false render(ctx){ switch(this.status){ case 1: //升空 ctx.save(); ctx.beginPath(); ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = this.opacity; ctx.translate(this.x, this.y); ctx.scale(0.8, 2.3); ctx.translate(-this.x, -this.y); ctx.fillStyle = this.color; ctx.arc(this.x + Math.sin(Math.PI * 2 * Math.random()) / 1.2, this.y, this.size, 0, Math.PI * 2, false); ctx.fill(); ctx.restore(); this.rise(); return true; break; case 2: //烟花消失阶段,等待炸裂 if(--this.wait <= 0){ this.opacity = 1; this.status = 3; } return true; break; case 3: //炸裂之后 渲染烟花微粒 ctx.save(); ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = this.opacity; ctx.fillStyle = this.color; for(let i = 0;i < this.particles.length;++i){ this.particles[i].render(ctx); } ctx.restore(); this.opacity -= 0.01; return this.opacity > 0; break; default: return false; } }}复制代码

放烟花

const canvas = {    init: function(){        //一些属性的设定 可以不用管    	this.setProperty();    	this.renderBg();    	    	//循环体 **主要    	this.loop();    },    setProperty: function(){    	this.fireworks = [];    	this.width = config.width;    	this.height = config.height;    	this.fireworkTime = (config.fireworkTime.min + (config.fireworkTime.max - config.fireworkTime.min) * Math.random()) | 0;        	this.bgCtx = document.querySelector('#bg').getContext('2d');    	this.fireworkCtx = document.querySelector('#firework').getContext('2d');    },    renderBg(){    	this.bgCtx.fillStyle = 'hsla(210, 60%, 5%, 0.9)'    	this.bgCtx.fillRect(0, 0, this.width, this.height);    },        loop(){    	requestAnimationFrame(this.loop.bind(this));    	this.fireworkCtx.clearRect(0, 0, this.width, this.height);                //随机创建烟花    	if(--this.fireworkTime <= 0){            this.fireworks.push(new Firework(config.fireworkOpt));            //每次到点之后重新设置烟花产生时间 (|0转化为整数)            this.fireworkTime = (config.fireworkTime.min + (config.fireworkTime.max - config.fireworkTime.min) * Math.random()) | 0;    	}        	for(let i = this.fireworks.length - 1; i >= 0; --i){    	    //渲染烟花 (若返回值为false则移除烟花)            !this.fireworks[i].render(this.fireworkCtx) && this.fireworks.splice(i,1);	    	}        }}canvas.init();复制代码

完善

此时烟花是这样的,感觉少了点小尾巴。

现在我们每一帧都是清除了画布,如果要加上小尾巴其实也很简单,每一帧都不要清除画布,而是覆盖一层新的有透明度的天空上去。

//canvas.loop方法// this.fireworkCtx.clearRect(0, 0, this.width, this.height);this.fireworkCtx.fillStyle = config.skyColor;this.fireworkCtx.fillRect(0,0,this.width,this.height);	复制代码

这时就变成这样了。

但是,还是缺少了在爆炸瞬间 天空变亮的场景。

那么在画烟花的时候,先会获取一下烟花的颜色以及透明度。

// *****Firework constructor// this.color = `hsla(${360 * Math.random() | 0},80%,60%,1)`;this.hue = 360 * Math.random() | 0;this.color = `hsla(${
this.hue},80%,60%,1)`;复制代码
// *****Firework 新增实例方法getSkyColor(){    const skyColor = {        //只有炸裂阶段才返回亮度    	lightness: this.status == 3 ? this.opacity : 0 ,    	hue: this.hue    };    return skyColor;}复制代码
// *****config 修改config的skyColor// skyColor: 'hsla(210, 60%, 5%, 0.2)',skyColor: 'hsla({hue}, 60%, {lightness}%, 0.2)',复制代码
// canvas.loop方法//this.fireworkCtx.fillStyle = config.skyColor;//每次替换色调与亮度值。this.fireworkCtx.fillStyle = config.skyColor.replace('{lightness}', 5 + this.skyColor.lightness * 15).replace('{hue}' , this.skyColor.hue);this.skyColor = { //新增    lightness: 0,    hue: 210};for(let i = this.fireworks.length - 1; i >= 0; --i){    //新增 天空颜色为最亮的烟花的颜色    this.skyColor = this.skyColor.lightness >= this.fireworks[i].getSkyColor().lightness ? this.skyColor : this.fireworks[i].getSkyColor();    !this.fireworks[i].render(this.fireworkCtx) && this.fireworks.splice(i,1);	}复制代码

到现在就算是大功告成了。

完整项目

如果觉得还不错,请star一个吧。

烟花制作参考链接

参考了上的很多作品。

主要参考 --- fireworks seen in the countryside

转载地址:http://tilxo.baihongyu.com/

你可能感兴趣的文章
谈谈JavaScript异步代码优化
查看>>
一文掌握关于Java数据结构所有知识点(欢迎一起完善)
查看>>
1.JSP-UEditor实现上传图片到项目外(SSM)
查看>>
2018三月个人前端社招面试经验总结
查看>>
Mysql索引失效的几种情况
查看>>
爬虫 Scrapy 学习系列之一:Tutorial
查看>>
WPF:Animation动画--KeySplineAnimation关键帧进度控制动画(2)
查看>>
【356天】每日项目总结系列093(2018.01.27)
查看>>
Node.js学习之路03——Buffer类初识
查看>>
bootstrap常用样式整理
查看>>
webpack从零开始第5课:模块篇
查看>>
一起学设计模式 - 迭代器模式
查看>>
Docker实践 - 超简单配置Ftp服务
查看>>
惊群问题及解决
查看>>
Javascript面向对象从入门到重新入门--关于继承
查看>>
python __new__ 和 __init__
查看>>
SpringCloud(第 025 篇)Zuul 路由后面的微服务挂了后,Zuul 提供了一种回退机制来应对熔断处理...
查看>>
听说2017你想写前端?
查看>>
react-router v4 使用 history 控制路由跳转
查看>>
基于gulp的一个简单的处理多个api域名环境的开发工作流
查看>>