HTML5のcanvasを使って花火アニメーションを作成してみました。
業務でweb系のフロント技術を扱ったことがないので、ちょっと触ってみました。
花火をアニメーションさせる仕組みは↓だけです。
・一定間隔ごとに花火を仕込む
・花火の火花を中心から離して描画(繰り返し)
一定間隔ごとに花火を仕込む
setInterval(花火を仕込む処理, 間隔[ms]) を使って繰り返しを処理。
・花火の色はランダム(乱数計算)
・花火1つにつき火花を150仕込む
・火花毎の離れ具合はランダム(離れる速度と方向を乱数計算)
花火の火花を中心から離して描画
requestAnimationFrame(描画処理)を使って連続描画。
・一回の描画で花火1つに含まれる火花を全て描画
・花火の色は白と交互(光っている様に見せる)
・描画の度に火花を離していく
・黒の透過を重ねる(消えていく様に見せる)
・一定以上離れれば終了
ソース
<canvas id="hanabi" width="600" height="400" style="background-color:black;">
</canvas>
<script>
setInterval(function () {
if((Math.random()*100) > 50){
return;
}
var hanabi = {
// 火花の数
'quantity' : 150,
// 火花の大きさ
'size' : 3,
// 減衰率
'circle' : 0.97,
// 重力
'gravity' : 1.1,
// 火花の速度
'speed' : 5,
// 位置
'top' : (Math.random()),
'left' : (Math.random()),
// 色
'color' : 'random'
};
Math.Radian = Math.PI * 2;
var hibana = [/*{
'pos_x' : left,
'pos_y' : top,
'vel_x' : Math.cos(angle) * speed,
'vel_y' : Math.sin(angle) * speed
}, ...*/];
var cvs = {
// canvas element
'elem' : undefined,
// canvas width(window max)
'width' : 0,
// canvas width(window height)
'height' : 0,
// 2d context
'ctx' : undefined,
// element offset(left)
'left' : 0,
// element offset(top)
'top' : 0,
// explode point(x)
'pos_x' : 0,
// explode point(y)
'pos_y' : 0
};
var frame = 0;
if (hanabi.color === 'random') {
var newcolor = (Math.random() * 0xFFFFFF | 0).toString(16);
hanabi.color = "#" + ("000000" + newcolor).slice(-6);
};
// キャンバス初期化
cvs.elem = document.getElementById('hanabi');
cvs.width = cvs.elem.width;
cvs.height = cvs.elem.height;
cvs.ctx = cvs.elem.getContext('2d');
cvs.left = cvs.elem.getBoundingClientRect ? cvs.elem.getBoundingClientRect().left : 0;
cvs.top = cvs.elem.getBoundingClientRect ? cvs.elem.getBoundingClientRect().top : 0;
// 火花を詰める
setTimeout(function () {
cvs.pos_y = cvs.height * hanabi.top;
cvs.pos_x = cvs.width * hanabi.left;
for (var i = 0; i < hanabi.quantity; ++i) {
var angle = Math.random() * Math.Radian;
var speed = Math.random() * hanabi.speed;
hibana.push({
'pos_x' : cvs.pos_x,
'pos_y' : cvs.pos_y,
'vel_x' : Math.cos(angle) * speed,
'vel_y' : Math.sin(angle) * speed
});
};
requestAnimationFrame(render);
}, 0)
// 花火の描画
function render () {
if (!hibana.length) {
return;
};
frame++;
cvs.ctx.fillStyle = (frame % 2) ? "rgba(255, 255, 255, 0.8)" : hanabi.color;
for (var i = 0, len = hibana.length; i < len; i++) {
var s = hibana[i];
s.pos_x += s.vel_x;
s.pos_y += s.vel_y;
s.vel_x *= hanabi.circle;
s.vel_y *= hanabi.circle;
s.pos_y += hanabi.gravity;
if (hanabi.size < 0.1 || !s.pos_x || !s.pos_y || s.pos_x > cvs.width || s.pos_y > cvs.height) {
hibana[i] = undefined;
return;
};
cvs.ctx.beginPath();
cvs.ctx.arc(s.pos_x, s.pos_y, hanabi.size, 0, Math.Radian, true);
cvs.ctx.fill();
};
hanabi.size *= hanabi.circle;
cvs.ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
cvs.ctx.fillRect(0, 0, cvs.width, cvs.height);
requestAnimationFrame(render);
}
}, 400);
</script>