Pixiシリーズ2回目です!
前回のPixi製スライダーに引き続き、今回はPixi.jsを使ってパーティクルアニメーションを作っていきます。
Pixi.jsでパーティクルアニメーションを作る
今回作るもの(完成版)
今回作るパーティクルアニメーションの完成版です。
各パーティクルがうようよランダムに浮遊しているような動きをしつつ、マウスを近づけるとマウスに追従してくるような動きを目指していきます。
また、それ以外にも色や大きさ・スピードなどもそれぞれのパーティクルで別々の動きをするように実装します。
0. ライブラリ・プラグインの読み込み
まずは以下のライブラリ・プラグインを読み込みます。
今回はPixi.js本体だけの読み込みです。
<!-- Pixi.js本体 -->
<script src="//cdnjs.cloudflare.com/ajax/libs/pixi.js/5.0.3/pixi.min.js"></script>
1. canvas追加
次に、前回と同様にcanvasを指定の要素に追加するためにnew PIXI.Application
を宣言します。
const Particle = {
init: function(options) {
this.options = options;
this.colors = ["0xE5493F","0x55C1FF","0x26B9A0", "0x5A52FF"]
this.particles = [];
this.maxTime = this.options.frequency * this.options.totalParticles;
this.timeToRecreate = false;
this.mouse = {
x: undefined,
y: undefined,
};
this.canvas = document.getElementById(this.options.targetId);
this.app = new PIXI.Application({
width: this.options.width,
height: this.options.height,
backgroundColor: '#111111',
antialias: true,
});
this.canvas.appendChild(this.app.view);
this.container = new PIXI.Container();
this.container.interactive = true;
this.app.stage.addChild(this.container);
},
};
Particle.init({
targetId: 'canvas', // canvasを追加するid
width: 900, // canvasの幅
height: 540, // canvasの高さ
totalParticles: 150, // パーティクルの総数
frequency: 100, // パーティクルを追加する頻度
});
2. 一定時間経過後にパーティクルを描画して生成
今回作るパーティクルは、完成版の画像ではわかりませんがcanvas中央あたりから一つづつパーティクルが現れる作りにしていきます。
そのため、setTimeout
を使って描画と生成をしていきます。
また、今回パーティクルを作るためにnew PIXI.ParticleContainer
を使っていますが、new PIXI.Graphics
をそのまま追加しようとするとエラーが返ってきてしまいます。内容を見るとどうやらPixiの仕様でSpriteに変換してくないといけないみたいなので、そのような処理をdraw
関数内で記述しています。
const Particle = {
init: function(options) {
// 〜省略
this.generate(this.options.totalParticles);
},
generate: function(num) {
for (let i = 0; i < num; i++) {
setTimeout(() => {
this.draw(); // 一定時間後に描画処理を走らせる
}, this.options.frequency * i);
}
},
draw: function() {
// 引数で色情報をランダムで渡し、別々の色情報を持ったパーティクルを貰います。
const graphic = this.particleGraphic(this.colors[this.randomIntFromInterval(0, this.colors.length - 1)]);
// 以下2行はSpriteに変換する処理
const texture = this.app.renderer.generateTexture(graphic);
const spriteParticle = new PIXI.Sprite(texture);
// 以下オプション
spriteParticle.anchor.set(0.5);
const size = 0.05 + Math.random() * 0.1;
spriteParticle.alpha = Math.random();
spriteParticle.scale.x = size;
spriteParticle.scale.y = size;
// パーティクル出現位置を画面中央付近に
spriteParticle.x = (this.options.width / 2) - ((Math.random() * 200) - (Math.random() * 200));
spriteParticle.y = (this.options.height / 2) - ((Math.random() * 200) - (Math.random() * 200));
spriteParticle.a = 0;
spriteParticle.s = ((1 + (8 * Math.random())) + (Math.random() * 1))/10;
// サイズが小さいパーティクルのスピードを速く、大きいパーティクルのスピードを遅く
spriteParticle.speed = (Math.floor( Math.random() * 4 ) + 3) / Math.floor((size * 100));
this.particles.push(spriteParticle);
// パーティクルコンテナの追加
this.particleContainer = new PIXI.ParticleContainer(
this.options.totalParticle,
{
scale: true,
position: true,
}
);
this.particleContainer.addChild(spriteParticle);
this.container.addChild(this.particleContainer);
},
particleGraphic: function(type) {
// パーティクル描画
const graphic = new PIXI.Graphics();
graphic.lineStyle(20, type, 10);
graphic.beginFill(0x111111, 0.7);
graphic.drawCircle(0, 0, 50);
graphic.endFill();
return graphic;
},
randomIntFromInterval(min,max){
return Math.floor(Math.random() * (max - min + 1) + min);
},
};
3. パーティクルの動きづけ
パーティクルの生成ができたら、いよいよ動きをつけていきます。
マウスに反応する動きもここで指定します。
const Particle = {
init: function(options) {
// 〜省略
// コンテナにマウスイベントを追加
this.container.on('mousemove', e => this.mouseAction(e));
setTimeout(() => {
this.timeToRecreate = true;
}, this.maxTime);
this.generate(this.options.totalParticles);
// 更新関数
this.update();
},
move: function() {
for(let i = 0; i < this.particles.length; i++) {
const particle = this.particles[i];
// 三角関数を使ってランダムな動きをさせる
particle.x += Math.cos(particle.a) * particle.s * particle.speed;
particle.y += Math.sin(particle.a) * particle.s * particle.speed;
particle.a += Math.random() * 0.4 - 0.2;
// 画面外に出た時の処理
if(particle.x > this.options.width + 10) {
particle.x = -5;
} else if(particle.x < 0 - 10) {
particle.x = this.options.width + 5;
} else if(particle.y > this.options.height + 10) {
particle.y = -5;
} else if(particle.y < 0 - 10) {
particle.y = this.options.height + 5;
}
// マウスの近くにあるパーティクルはマウスに近寄ってくる
if (this.mouse.x - particle.x < 80 && this.mouse.x - particle.x > -80
&& this.mouse.y - particle.y < 80 && this.mouse.y - particle.y > -80) {
particle.x += (this.mouse.x - particle.x) * particle.speed * 0.02;
particle.y += (this.mouse.y - particle.y) * particle.speed * 0.02;
}
}
},
update: function() {
this.move();
if (this.timeToRecreate) {
if (this.particles.length < this.options.totalParticles) {
// パーティクルが全て生成し終わったら、一定時間ごとにまた生成する
this.generate(1);
}
}
requestAnimationFrame(this.update.bind(this));
},
mouseAction: function(e) {
// マウスの位置を取得
this.mouse.x = e.data.global.x;
this.mouse.y = e.data.global.y;
},
};
終わりに
いかがでしたか?意外と簡単にパーティクルが作れました。
パーティクルは一見動きが複雑に見えるため実装も難しく感じてしまいますが、仕組みさえ理解してしまえばそこまで量も多くない記述で実装できてしまいます。
また、一番のポイントは色や大きさ・スピードなどにどんな値を持たせるかで、どんな見栄え・印象になるかが変わってきますので色んな値を入れて試してみてください。
次はPixiを使ってどんな実装をしていこうかしら。
では、また〜