流れ星みたいにスーッと流れて消えるような描画をキャンバスのglobalAlphaを利用して再現してみました。
残像の処理
キャンバスのグラフィックコンテキストのglobalAlphaというプロパティを使うと残像のような効果を生み出せます。
globalAlphaは端的に言うと透明度です。
デフォルト値は1.0で不透明となっています。今回は背景色を黒で描画する前にglobalAlphaを0.1として透明にしておき、背景を黒で塗りつぶしています。こうすることで直前に描画した星が少し透明掛かった描画となり、残像のような効果を生み出しています。
g.globalAlpha = 0.1;
g.fillStyle = "black";
g.fillRect(0,0, canvas.width, canvas.height);
g.globalAlpha = 1;
g は let g = canvas.getContext(“2d”) として定義
ソースコード
JavaScript部分は、流れ星クラス(star.js)と全体の管理(main.js)に分かれています。
HTML部分
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,user-scalable=yes">
<script src="star.js" type="text/javascript"></script>
<script src="main.js" type="text/javascript"></script>
<title>流れ星</title>
<style>
*{
margin: 0;
padding: 0;
}
#clock{
display: block;
}
html, body, #wrapper{
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="wrapper">
<canvas id="canvas" width="" height=""></canvas>
</div>
</body>
</html>
JavaScript部分
star.js
流れ星クラスです。
コンストラクタ内のlife_time変数に星の寿命を設定しておき(17行目)、updateメソッド(31行目~40行目)で、星の寿命を超えたらalive変数をfalseにしておきます。
クラス内でインスタンスを消去することが出来なかったため、後述のmain.jsで外部からalive変数を参照してfalseだったときクラスのインスタンスをdeleteするようにしています。
/*
* star.js: 流れ星クラス
*/
class Star{
// コンストラクタ
constructor(){
this.x = Math.floor(Math.random() * canvas.width); // x座標
this.y = Math.floor(Math.random() * canvas.height / 2); // y座標
this.r = Math.floor(Math.random() * 10) + 1; // 半径
this.angle = Math.floor(Math.random() * 160) + 10; // 角度
this.speed = Math.random() * 5 + 5; // 速度
this.vx = this.speed * Math.cos(this.angle / 180 * Math.PI); // x方向のスピード
this.vy = this.speed * Math.sin(this.angle / 180 * Math.PI); // y方向のスピード
this.alive = true; // 星の消滅可否
this.start_time = new Date().getTime(); // 星が生まれた時の時刻
this.life_time = Math.random() * 1500; // 星の寿命を設定
}
// 描画メソッド
draw(){
g.beginPath();
g.fillStyle = STAR_COLOR;
g.arc(this.x, this.y, this.r, 0, Math.PI*2, false); // 塗りつぶし円を描画
g.fill();
}
// 移動メソッド
move(){
this.x += this.vx;
this.y += this.vy;
}
// 更新処理
update(){
this.move();
this.draw();
// 消滅判定
let left_time = new Date().getTime() - this.start_time; // 経過時間
this.r -= (left_time / 10000.0); // 徐々に星を小さくする
if(left_time > this.life_time || this.r <= 0.0){ // 寿命または星の半径が0よりちいさくなったら
this.alive = false; // 消滅させる
}
}
}
main.js
全体の管理をおこなっています。
8行目で定義しているobjects配列に流れ星(Starクラス)のインスタンスを代入しています。
function make_star(){
let star = new Star();
objects.push(star);
}
描画の更新は、全体の描画やオブジェクトの消滅をおこなうmainLoop関数をrequestAnimationFrame関数を使い一定間隔で呼び出すことで実現しています。
requestAnimationFrame(gameLoop);
ブラウザ画面のサイズにキャンバスサイズを合わせる処理をして端末がどんなサイズでも違和感なく描画するようにしています。詳しくは以下の記事を参考にしてください。
/*
* グローバル変数
*/
let wrapper = null; // キャンバスの親要素
let canvas = null; // キャンバス
let g = null; // コンテキスト
let $id = function(id){ return document.getElementById(id); }; // DOM取得用
let objects = []; // 流れ星を格納するオブジェクト配列
/*
* 定数
*/
const BACKGROUND_COLOR = "black"; // 背景色
const STAR_COLOR = "white"; // 星の色
/*
* キャンバスのサイズをウインドウに合わせて変更
*/
function getSize(){
// キャンバスのサイズを再設定
canvas.width = wrapper.offsetWidth;
canvas.height = wrapper.offsetHeight;
}
/*
* リサイズ時
*/
window.addEventListener("resize", function(){
getSize();
});
/*
* 流れ星を生成する
*/
function make_star(){
let star = new Star();
objects.push(star);
}
/*
* ゲームループ
*/
function gameLoop(){
// たまに流れ星を生成
if(Math.floor(Math.random() * 100) == 1){
// いつまでも残っている残像を消すため
g.fillStyle = BACKGROUND_COLOR;
g.fillRect(0,0, canvas.width, canvas.height);
// 流れ星を生成
make_star();
}
// オブジェクトを更新
for(let i in objects){
objects[i].update();
// 星の寿命チェック
if(objects[i].alive == false){
delete objects[i]; // 流れ星消滅
}
}
// 半透明の背景色で画面クリア(残像のようにみせるため)
g.globalAlpha = 0.1;
g.fillStyle = BACKGROUND_COLOR;
g.fillRect(0,0, canvas.width, canvas.height);
g.globalAlpha = 1; // 元に戻して円を描画
// gameLoopを再帰:いわゆる「ゲームループ」と呼ばれる一定間隔で描画の更新を行う処理
requestAnimationFrame(gameLoop);
}
/*
* 起動時の処理
*/
window.addEventListener("load", function(){
// キャンバスの親要素情報取得(親要素が無いとキャンバスのサイズが画面いっぱいに表示できないため)
wrapper = $id("wrapper");
// キャンバス情報取得
canvas = $id("canvas");
g = canvas.getContext("2d");
// キャンバスをウインドウサイズにする
getSize();
// とりあえず流れ星を5つ生成
for(i=0; i<5; i++){
make_star();
}
// ゲームループスタート
gameLoop();
});
流れ星って、見ようとするとなかなか見ることができませんよね。
コメント