WEBOPIXEL

HTML5+Canvasでランダムな方向に移動するカラフルパーティクル

Posted: 2017.07.19 / Category: javascript / Tag: ,

以前の記事で「HTML5+Canvasでパーティクルっぽいのを作ってみる」というのを作成しましたが、横の移動だけだったのであまり面白くありませんでした。
今回は横だけではなく縦や斜めにの方向にランダムにパーティクルを移動させたいと思います。

Sponsored Link

HTML + CSS

画面幅いっぱいに表示させたいのでcanvasをdivで囲んでます。
canvas-txtにcanvasの上に表示されるコンテンツを配置します。

html

<div id="canvas-wrap">
<canvas id="canvas-container"></canvas>
<div id="canvas-txt"><p>CONTENTS</p></div>
</div>

CSSです。
今回はwidth,heightとも100%を指定してますが、pxで数値を指定しても問題ありません。

css

html,body {
	height: 100%;
}

#canvas-wrap {
	width: 100%;
	height: 100%;
    position: relative;
}
#canvas-txt {
	font-size: 60px;
	display: flex;
	justify-content: center;
	align-items: center;
	position: absolute;
	left: 0;
	top: 0;
	width: 100%;
	height: 100%;
}

画面サイズいっぱいにCanvasを配置する

単純にcanvasを100%にすると、比率が崩れてしまいます。
一旦親のサイズを取得して設定することで100%表示にすることができます。

javascript

var canvasWrap = document.querySelector('#canvas-wrap');
var canvas = document.querySelector('#canvas-container');
var ctx = canvas.getContext('2d');
canvas.setAttribute('width', canvasWrap.offsetWidth);
canvas.setAttribute('height', canvasWrap.offsetHeight);

ランダムな方向に移動するパーティクル

ランダムにパーティクルを配置して、ランダムな方向に移動させてみます。

See the Pen Random Particle by webopixel (@webopixel) on CodePen.

javascript

window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
	window.webkitRequestAnimationFrame ||
	window.mozRequestAnimationFrame ||
	window.oRequestAnimationFrame ||
	window.msRequestAnimationFrame ||
	function (callback) {
		window.setTimeout(callback, 1000 / 60);
	};
})();

window.onload = function() {
    var canvasWrap = document.querySelector('#canvas-wrap');
    var canvas = document.querySelector('#canvas-container');
    var ctx = canvas.getContext('2d');

    var center = {};    // Canvas中央
    var dots = [];      // パーティクル配列
    var density = 70;  //パーティクルの数
    var colors = ['#eeb900', '#6DD0A5', '#f799db'];
    var baseSize = 3;   // 大きさ
    var baseSpeed = 10; // スピード

    var Dot = function () {
        this.size = Math.floor( Math.random() * 6 ) + baseSize; //大きさ
        this.color = colors[~~(Math.random() * 3)]; //色
        this.speed = this.size / baseSpeed; // 大きさによって速度変更
        this.pos = {   // 位置
            x: Math.random() * canvas.width,
            y: Math.random() * canvas.height
        };
        var rot = Math.random() * 360;  // ランダムな角度
        var angle = rot * Math.PI / 180;

        this.vec = {    // 移動方向
            x: Math.cos(angle) * this.speed,
            y: Math.sin(angle) * this.speed
        };
    };
    Dot.prototype = {
        update: function() {
            this.draw();

            this.pos.x += this.vec.x;
            this.pos.y += this.vec.y;

            // 画面外に出たら反対へ再配置
            if(this.pos.x > canvas.width + 10) {
                this.pos.x = -5;
            } else if(this.pos.x < 0 - 10) {
                this.pos.x = canvas.width + 5;
            } else if(this.pos.y > canvas.height + 10) {
                this.pos.y = -5;
            } else if(this.pos.y < 0 - 10) {
                this.pos.y = canvas.height + 5;
            }
        },

        draw: function() {
            ctx.fillStyle = this.color;
            ctx.beginPath();
            ctx.arc(this.pos.x, this.pos.y, this.size, 0, 2 * Math.PI, false);
            ctx.fill();
        }
    };

    function update() {
        requestAnimFrame(update);
        // 描画をクリアー
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

        for (var i = 0; i < density; i++) {
            dots[i].update();
        }
    }

    function init() {
        // canvasにコンテンツサイズをセット
        canvas.setAttribute("width", canvasWrap.offsetWidth);
        canvas.setAttribute("height", canvasWrap.offsetHeight);

        // canvas中央をセット
        center.x = canvas.width / 2;
        center.y = canvas.height / 2;

        // densityの数だけパーティクルを生成
        for (var i = 0; i < density; i++) {
            dots.push(new Dot());
        }
        update();
    }
    init();
}

ほとんどがCanvasアニメーションの基本的な部分になりますが、重要なところは32行目あたりからです。
横方向の場合単純にx軸の値を変更していけばよかったのですが、斜めに移動させたいときはy軸も変動させる必要があります。
角度から移動方向のx、yを決めるには三角関数で結果がでます。
実際は難しい事考える必要はなく、例えば45度に移動したいときは下記のようなコードになります。

var angle = 45 * Math.PI / 180;
this.vec = {
	x: Math.cos(angle),
	y: Math.sin(angle)
};

vecが移動方向になりますので、updateのときにこと値を足す事で指定した方向へ移動することができます。

this.pos.x += this.vec.x;
this.pos.y += this.vec.y;

中央から拡散するパーティクル

中央から外側に拡散するような動きをしてみます。
Dot部分のみ変更します。

See the Pen Diffusion Particle by webopixel (@webopixel) on CodePen.

javascript

var Dot = function () {
    this.size = Math.floor( Math.random() * 6 ) + baseSize; //大きさ
    this.color = colors[~~(Math.random() * 3)]; //色
    this.speed = this.size / baseSpeed; // 大きさによって速度変更

    this.pos = {   // 位置
        x: canvas.width / 2,
        y: canvas.height / 2
    };

    var rot = Math.random() * 360;  // ランダムな角度
    var angle = rot * Math.PI / 180;

    this.vec = {    // 移動方向
        x: Math.cos(angle) * this.speed,
        y: Math.sin(angle) * this.speed
    };

    // ランダムに配置
    var startRandom = Math.random();
    this.pos.x += this.vec.x * (startRandom * center.x);
    this.pos.y += this.vec.y * (startRandom * center.y);
};

Dot.prototype = {
    update: function() {
        this.pos.x += this.vec.x;
        this.pos.y += this.vec.y;

        if(this.pos.x > canvas.width + baseSize
                || this.pos.x < 0 - baseSize
                || this.pos.y > canvas.height + baseSize
                || this.pos.y < 0 - baseSize) {
            this.pos.x = center.x;
            this.pos.y = center.y;
        }
    },
    draw: function() {
        ctx.fillStyle = this.color;
        ctx.beginPath();
        ctx.arc(this.pos.x, this.pos.y, this.size, 0, 2 * Math.PI, false);
        ctx.fill();
    }
};

開始位置のposをcanvas中央に指定して、画面外に出た時も再配置も同じくcanvas中央にしているだけですね。

この作りだとパーティクルの数を直接指定しているので、画面サイズによって密度が変わってきてしまいます。
この部分は画面サイズによって変動させるなどの処理が必要になってくると思います。