最近感覺canvas挺有意思的,在業餘時間沒事研究了一下,參考過網上一些思路,話不多說,開始啦。 github地址:https://github.com/aWhiteBear/fireworks 演示地址:https://awhitebear.github.io/fireworks/ 圖片效果如下: ...
最近感覺canvas挺有意思的,在業餘時間沒事研究了一下,參考過網上一些思路,話不多說,開始啦。
github地址:https://github.com/aWhiteBear/fireworks
演示地址:https://awhitebear.github.io/fireworks/
圖片效果如下:(右上角能顯示FPS,是時候看看你電腦的性能了哈哈~)
先說一下思路吧,其實很簡單,煙花分為兩個部分:1.竄天猴。2.爆炸效果。 當穿天猴的豎直方向的速度為0的時候,讓它爆炸。
下麵是一些常量,後面類中會用到的
1 const canvas = document.getElementById('canvasNode'); 2 const ctx = canvas.getContext('2d'); 3 canvas.width = window.innerWidth; 4 canvas.height = window.innerHeight; 5 const CANVAS_WIDTH = canvas.width; 6 const CANVAS_HEIGHT = canvas.height; 7 const GRAVITATIONAL = 2.5; // 模擬重力加速度 8 const AIR_RESISTANCE = 1; // 模擬空氣阻力 9 const EVERY_FIREWORK_TIME = 3; // 每個煙花的持續時間,單位:秒 10 const FRAME = 60;
class FlyingMonkey{ // 竄天猴,發射升空的,目前只能垂直發射 constructor(x,y,speedX,speedY){ this.x = x; // x,y 是竄天猴的位置坐標 this.y = y; this.speedX = speedX; this.speedY = speedY; this.opacity = 1; this.count = 50; // 竄天猴和其尾巴由這50個圓繪製而成 for(let i=0;i<this.count;i++){ this.createCircle(i); } } createCircle(i) { // 創建竄天猴的圈圈尾巴 ctx.beginPath(); ctx.save(); ctx.globalCompositeOperation = 'lighter'; ctx.fillStyle = `rgba(245,123,63,${this.opacity})`; ctx.arc(this.x + (Math.random()-0.5) * i/10 + i/this.count * this.speedX, this.y + i/this.count * this.speedY ,8 - (6 * i/this.count),0,2 * Math.PI); ctx.fill(); ctx.restore(); ctx.closePath(); } }
上面是竄天猴類,就是最開始向上發射的煙花,下麵煙花類
class Firework { // 煙花,爆炸的 constructor(x,y,speedX,speedY){ this.x = x; this.y = y; this.speedX = speedX; this.speedY = speedY; this.opacity = 1; this.count = 500; // 煙花的爆炸效果由500個點組成 this.AllFireworks = []; this.createAllFirework(); Launch.arrFirework.push(this); } createAllFirework(){ let r = Math.floor(Math.random()*256), g = Math.floor(Math.random()*256) , b =Math.floor(Math.random()*256); for(let i=0;i<this.count;i++){ this.AllFireworks.push({ r,g,b, x:this.x, y:this.y, opacity:1, speedX:this.speedX * i/this.count*10 *(Math.random()-0.5), speedY:this.speedY * i/this.count*10 *(Math.random()-0.5) }); } this.updateAllFirework(); } updateAllFirework(){ for(let i=0;i<this.AllFireworks.length;i++){ let {r,g,b,x,y,speedX,speedY,opacity} = this.AllFireworks[i]; this.AllFireworks[i].y = y - speedY/FRAME; this.AllFireworks[i].x = x - speedX/FRAME; this.AllFireworks[i].opacity = opacity - 1/ FRAME / EVERY_FIREWORK_TIME; this.AllFireworks[i].speedY = speedY - GRAVITATIONAL; if(Math.abs(speedX)>3/FRAME) { // 速度<= 3/FRAME 認為停止了 this.AllFireworks[i].speedX = speedX - (speedX>0?AIR_RESISTANCE:(AIR_RESISTANCE*(-1))); } else { this.AllFireworks[i].speedX = 0; } ctx.beginPath(); ctx.save(); ctx.globalCompositeOperation = 'lighter'; ctx.fillStyle = `rgba(${r},${g},${b},${this.AllFireworks[i].opacity})`; ctx.arc(this.AllFireworks[i].x , this.AllFireworks[i].y ,2,0,2 * Math.PI); ctx.fill(); ctx.restore(); ctx.closePath(); } } }
下麵是start類,用來發射竄天猴
class Start{ constructor(x,y,speedX,speedY){ Launch.arrFlyingMonkey.push(this); this.x = x; this.y = y; this.speedX = speedX; this.speedY = speedY; this.begin(); } begin(){ this.y = this.y - this.speedY/FRAME; // 速度減小 this.x = this.x - this.speedX/FRAME; this.speedY = this.speedY - GRAVITATIONAL; new FlyingMonkey(this.x, this.y, this.speedX, this.speedY); } }
下麵是發射類,是用【requestAnimationFrame】來渲染的動畫
class Launch{ // 發射 constructor(){ this.fps=0; this.sum=0;// 幀數計數器 60幀一迴圈 this.draw = this.draw.bind(this); this.draw(); } draw(){ ctx.clearRect(0,0,CANVAS_WIDTH,CANVAS_HEIGHT); this.updateFps(); Launch.arrFlyingMonkey.forEach((item,i)=>{ item.begin(); if(item.speedY < 0){ Launch.arrFlyingMonkey.splice(i,1); new Firework(item.x,item.y,7*7,5*7); // 煙花寬高比:7:5 } }); Launch.arrFirework.forEach((item,i)=>{ item.updateAllFirework(); }); if(Launch.arrFirework.length>5){ // 清理arrFirework,避免占用過多記憶體,其實還可以通過 EVERY_FIREWORK_TIME 和 Launch.timer 更及時清理。length > EVERY_FIREWORK_TIME/Launch.timer Launch.arrFirework.shift(); } requestAnimationFrame(this.draw); } updateFps(){ if(this.sum++>=60){ this.sum = 0; let nowTime = new Date().getTime(); this.fps = 60/(nowTime - Launch.lastTime) *1000; Launch.lastTime = nowTime; } ctx.save(); ctx.fillStyle = 'red'; ctx.font="20px Arial"; ctx.fillText(`FPS: ${~~this.fps}`,CANVAS_WIDTH - 100,50); ctx.restore(); } }
然後添加Launch靜態屬性
/** 添加Launch靜態屬性*/ Launch.arrFlyingMonkey = []; Launch.arrFirework = []; Launch.timer = setInterval(()=>{ new Start(CANVAS_WIDTH * (Math.random() * 0.8 + 0.1),CANVAS_HEIGHT * 0.9,0,300 *(Math.random()*0.5 + 1)); },1500); Launch.lastTime = new Date().getTime();
最後在 new Launch(); 就能發射出去啦。
代碼還有好多可以優化的地方,在一些手機瀏覽器上會出現fps越來越低得到情況,畫面會變卡,以後可能的話要在進行優化一下,也可以和大家討論一下如何優化會更好,可以在評論區指導一下呀,感謝大家提出寶貴的意見~