JavaScriptとCanvasで、Webページに花火アニメーションを表示する方法を紹介します。
このページでは、Sin/Cosを使った放射状の粒子描画や、グラデーションによる発光表現、複数段階の爆発処理など、本格的な花火演出をコピペで実装できるコードを掲載しています。
夏のイベントページや、季節感のある演出にぴったりのアニメーション素材です。
HTML,Javascript
<canvas class="Hanabi" style="width:100%;max-width:800px;height:200px;"></canvas>
<script>
TMamHanabi=function(){
this.can=[];
this.ctx=[];
this.width=[];
this.height=[];
this.position=[];
this.basewh=[];
this.basesp=[];
this.limit=[];
this.flag=[];
this.child=[];
this.init=function(){
this.can=document.querySelectorAll(".Hanabi");
for(let i=0;i<this.can.length;i++){
this.ctx[i]=this.can[i].getContext("2d", {willReadFrequently:true});
//見た目のサイズを取得
let style = window.getComputedStyle(this.can[i]);
this.width[i]=parseInt(style.width);
this.height[i]=parseInt(style.height);
this.basewh[i]=Math.min(this.width[i],this.height[i]);
this.basesp[i]=this.height[i]/200;
//内部のサイズの設定
this.can[i].setAttribute("width",this.width[i]+"px");
this.can[i].setAttribute("height",this.height[i]+"px");
this.position[i]=[];
this.position[i]["x"]=this.width[i]/2;
this.position[i]["y"]=this.height[i];
this.limit[i]=this.height[i]*1/3+Math.random()*this.height[i]/3-this.height[i]/6;
this.flag[i]=0;
this.child[i]=[];
this.reset(i);
this.ctx[i].fillStyle="#000";
this.ctx[i].fillRect(0,0,this.width[i],this.height[i]);
}
window.addEventListener("resize",this.resize.bind(this));
}
this.resize=function(){
for(let i=0;i<this.can.length;i++){
//見た目のサイズを取得
let style = window.getComputedStyle(this.can[i]);
this.width[i]=parseInt(style.width);
this.height[i]=parseInt(style.height);
this.basewh[i]=Math.min(this.width[i],this.height[i]);
this.basesp[i]=this.height[i]/200;
this.can[i].setAttribute("width",this.width[i]+"px");
this.can[i].setAttribute("height",this.height[i]+"px");
}
}
this.reset=function(i){
this.position[i]["x"]=this.width[i]/3+this.width[i]*Math.random()/3;
this.position[i]["y"]=this.height[i];
this.limit[i]=this.height[i]*1/3+Math.random()*this.height[i]/3-this.height[i]/6;
this.flag[i]=0;
let ct=0;
for(let j=0;j<12;j++){
for(let k=0;k<(j+1)*8;k++){
let deg=360/((j+1)*8);
this.child[i][ct]=[];
this.child[i][ct]["x"]=this.position[i]["x"];
this.child[i][ct]["y"]=this.limit[i];
let rd=Math.random();
this.child[i][ct]["vx"]=Math.cos(deg*k*Math.PI/180)*(j+0.8+rd*Math.random()*0.4)*this.basewh[i]/640/5;
this.child[i][ct]["vy"]=Math.sin(deg*k*Math.PI/180)*(j+0.8+rd*Math.random()*0.4)*this.basewh[i]/640/5;
let rr,gg,bb;
rr=Math.floor(Math.random()*j/2+255-j*2);
gg=Math.floor(Math.random()*j/4+255-j*8);
bb=Math.floor(Math.random()*j/16+255-j*16);
this.child[i][ct]["c"]=''+rr+','+gg+','+bb+'';
ct++;
}
}
}
this.draw=function(){
for(let i=0;i<this.ctx.length;i++){
this.ctx[i].fillStyle="#000";
this.ctx[i].fillRect(0,0,this.width[i],this.height[i]);
if(this.flag[i]==0){
if(this.position[i]["y"]<this.limit[i]){
this.flag[i]=1;
}else{
this.position[i]["y"]-=this.basesp[i];
}
let x=this.position[i]["x"];
let y=this.position[i]["y"];
let r=this.basewh[i]/80+Math.random()*this.basewh[i]/80;
var rgrd = this.ctx[i].createRadialGradient(x, y, r, x, y, 0);
let rr=Math.floor(Math.random()*5+250);
let gg=Math.floor(Math.random()*55+200);
let bb=Math.floor(Math.random()*55+200);
rgrd.addColorStop(0.0, 'rgba('+rr+','+gg+','+bb+',0.0)');
rgrd.addColorStop(0.2, 'rgba('+rr+','+gg+','+bb+',0.2)');
rgrd.addColorStop(1.0, 'rgba('+rr+','+gg+','+bb+',0.8)');
this.ctx[i].beginPath();
this.ctx[i].arc(x, y, r, 0, 2 * Math.PI);
this.ctx[i].fillStyle = rgrd;
this.ctx[i].fill();
}else if(this.flag[i]>0){
if(this.flag[i]>100){this.flag[i]=-50;return;}
var r;
for(let j=0;j<this.child[i].length;j++){
r=(1+Math.random()/2)*this.basewh[i]/128;
this.child[i][j]["vy"]+=this.basewh[i]/160/200*(1+Math.random()/8);
this.child[i][j]["x"]+=this.child[i][j]["vx"];
this.child[i][j]["y"]+=this.child[i][j]["vy"];
let rgrd = this.ctx[i].createRadialGradient(
this.child[i][j]["x"], this.child[i][j]["y"], r,
this.child[i][j]["x"], this.child[i][j]["y"], 0
);
let op=(100-this.flag[i])/100;
rgrd.addColorStop(0.0, 'rgba('+this.child[i][j]["c"]+','+0.0*op+')');
rgrd.addColorStop(0.6, 'rgba('+this.child[i][j]["c"]+','+0.8*op+')');
rgrd.addColorStop(1.0, 'rgba('+this.child[i][j]["c"]+','+1.0*op+')');
this.ctx[i].beginPath();
this.ctx[i].arc(this.child[i][j]["x"], this.child[i][j]["y"], r, 0, 2 * Math.PI);
this.ctx[i].fillStyle = rgrd;
this.ctx[i].fill();
}
this.flag[i]++;
}else{
this.flag[i]++;
if(this.flag[i]==0){
this.reset(i);
}
}
}
}
window.addEventListener("DOMContentLoaded",this.init.bind(this));
setInterval(this.draw.bind(this),20);
}
MamHanabi=new TMamHanabi();
</script>
