canvas实现烟花的示例代码
程序员文章站
2022-04-25 18:19:33
这篇文章主要介绍了canvas实现烟花的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 20-01-16...
前言:马上过年了,我打算在后台里面偷偷地埋个新春祝福+放烟花的彩蛋。项目是基于react+typescript的,因此最后封装成了一个组件,设置好开启时间就可以显示了。
目录结构
目录结构大致如下
我们将烟花分为两个阶段,一个是未炸开持续上升时期,另一个是炸开后分散的时期。
其中vector表示一个坐标,particle表示一个烟花的亮点,firewor表示烟花未炸开时持续上升的亮点。index.tsx就是组件了,绘制了canvas,并执行了动画。
vector
这个坐标就很简单,后面涉及到位置的变更都可以使用它的add方法进行偏移操作
export default class vector { constructor(public x: number, public y: number) {} add(vec2: {x: number; y: number}) { this.x = this.x + vec2.x; this.y = this.y + vec2.y; } }
particle
初始创建的时候给个坐标,后续每次更新的时候控制y坐标下落,gravity变量就是下落的值。timespan用于控制烟花展示的时长
import vector from './vector'; export default class particle { pos: vector = null; vel: {x: number; y: number} = null; dead: boolean = false; start: number = 0; ctx: canvasrenderingcontext2d = null; constructor(pos: {x: number; y: number}, vel: {x: number; y: number}, ctx: canvasrenderingcontext2d) { this.pos = new vector(pos.x, pos.y); this.vel = vel; this.dead = false; this.start = 0; this.ctx = ctx; } update(time: number, gravity: number) { let timespan = time - this.start; if (timespan > 500) { this.dead = true; } if (!this.dead) { this.pos.add(this.vel); this.vel.y = this.vel.y + gravity; } } draw() { if (!this.dead) { this.drawdot(this.pos.x, this.pos.y, math.random() > 0.5 ? 1 : 2); } } drawdot(x: number, y: number, size: number) { this.ctx.beginpath(); this.ctx.arc(x, y, size, 0, math.pi * 2); this.ctx.fill(); this.ctx.closepath(); } }
firework
生成随机的hsl颜色,hsl(' + rndnum(360) + ', 100%, 60%)
;firework每次上升的距离是一个递减的过程,我们初始设置一个上升的距离,之后每次绘制的时候,这个距离减gravity,当距离小于零的时候,说明该出现烟花绽放的动画了。
import vector from './vector'; import particle from './particle'; let rnd = math.random; function rndnum(num: number) { return rnd() * num + 1; } export default class firework { pos: vector = null; vel: vector = null; color: string = null; size: number = 0; dead: boolean = false; start: number = 0; ctx: canvasrenderingcontext2d = null; gravity: number = null; exparticles: particle[] = []; explen: number = 100; rootshow: boolean = true; constructor(x: number, y: number, gravity: number, ctx: canvasrenderingcontext2d) { this.pos = new vector(x, y); this.vel = new vector(0, -rndnum(10) - 3); this.color = 'hsl(' + rndnum(360) + ', 100%, 60%)'; this.size = 4; this.dead = false; this.start = 0; this.ctx = ctx; this.gravity = gravity; } update(time: number, gravity: number) { if (this.dead) { return; } this.rootshow = this.vel.y < 0; if (this.rootshow) { this.pos.add(this.vel); this.vel.y = this.vel.y + gravity; } else { if (this.exparticles.length === 0) { for (let i = 0; i < this.explen; i++) { let randomr = rndnum(5); let randomx = -rndnum(math.abs(randomr) * 2) + math.abs(randomr); let randomy = math.sqrt(math.abs(math.pow(randomr, 2) - math.pow(randomx, 2))) * (math.random() > 0.5 ? 1 : -1); this.exparticles.push(new particle(this.pos, new vector(randomx, randomy), this.ctx)); this.exparticles[this.exparticles.length - 1].start = time; } } let numofdead = 0; for (let i = 0; i < this.explen; i++) { let p = this.exparticles[i]; p.update(time, this.gravity); if (p.dead) { numofdead++; } } if (numofdead === this.explen) { this.dead = true; } } } draw() { if (this.dead) { return; } this.ctx.fillstyle = this.color; if (this.rootshow) { this.drawdot(this.pos.x, this.pos.y, this.size); } else { for (let i = 0; i < this.explen; i++) { let p = this.exparticles[i]; p.draw(); } } } drawdot(x: number, y: number, size: number) { this.ctx.beginpath(); this.ctx.arc(x, y, size, 0, math.pi * 2); this.ctx.fill(); this.ctx.closepath(); } }
fireworkcomponent
组件本身就很简单了,生成和绘制firework。我们在这里面可以额外加一些文字
import react from 'react'; import firework from './firework'; import {autobind} from 'core-decorators'; let rnd = math.random; function rndnum(num: number) { return rnd() * num + 1; } interface proptypes { onclick?: () => void; } @autobind class fireworkcomponent extends react.component<proptypes> { canvas: htmlcanvaselement = null; ctx: canvasrenderingcontext2d = null; snaptime: number = 0; fireworks: firework[] = []; gravity: number = 0.1; componentdidmount() { this.canvas = document.queryselector('#fireworks'); this.canvas.width = window.innerwidth; this.canvas.height = window.innerheight; this.ctx = this.canvas.getcontext('2d'); this.init(); this.draw(); } init() { let numoffireworks = 20; for (let i = 0; i < numoffireworks; i++) { this.fireworks.push(new firework(rndnum(this.canvas.width), this.canvas.height, this.gravity, this.ctx)); } } update(time: number) { for (let i = 0, len = this.fireworks.length; i < len; i++) { let p = this.fireworks[i]; p.update(time, this.gravity); } } draw(time?: number) { this.update(time); this.ctx.fillstyle = 'rgba(0,0,0,0.3)'; this.ctx.fillstyle = 'rgba(0,0,0,0)'; this.ctx.clearrect(0, 0, this.canvas.width, this.canvas.height); this.ctx.font = 'bold 30px cursive'; this.ctx.fillstyle = '#e91818'; let text = 'xx项目组给您拜个早年!'; let textwidth = this.ctx.measuretext(text); this.ctx.filltext(text, this.canvas.width / 2 - textwidth.width / 2, 200); text = '在新年来临之际,祝您:'; textwidth = this.ctx.measuretext(text); this.ctx.filltext(text, this.canvas.width / 2 - textwidth.width / 2, 260); text = '工作顺利,新春快乐!'; this.ctx.font = 'bold 48px stcaiyun'; this.ctx.fillstyle = 'orangered'; textwidth = this.ctx.measuretext(text); this.ctx.filltext(text, this.canvas.width / 2 - textwidth.width / 2, 340); this.ctx.fillstyle = 'gray'; this.ctx.font = '18px arial'; text = '点击任意处关闭'; textwidth = this.ctx.measuretext(text); this.ctx.filltext(text, this.canvas.width - 20 - textwidth.width, 60); this.snaptime = time; this.ctx.fillstyle = 'blue'; for (let i = 0, len = this.fireworks.length; i < len; i++) { let p = this.fireworks[i]; if (p.dead) { p = this.fireworks[i] = new firework( rndnum(this.canvas.width), this.canvas.height, this.gravity, this.ctx ); p.start = time; } p.draw(); } window.requestanimationframe(this.draw); } render() { return ( <canvas id="fireworks" onclick={this.props.onclick} style={{position: 'fixed', zindex: 99, background: 'rgba(0,0,0, 0.8)'}} width="400" height="400"></canvas> ); } } export default fireworkcomponent;
大致效果
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。