13
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Pixi連載#2】Pixi.jsでパーティクルアニメーションを作る

Last updated at Posted at 2019-06-23

Pixiシリーズ2回目です!
前回のPixi製スライダーに引き続き、今回はPixi.jsを使ってパーティクルアニメーションを作っていきます。

Pixi.jsでパーティクルアニメーションを作る

今回作るもの(完成版)

particle.mov.gif

今回作るパーティクルアニメーションの完成版です。
各パーティクルがうようよランダムに浮遊しているような動きをしつつ、マウスを近づけるとマウスに追従してくるような動きを目指していきます。
また、それ以外にも色や大きさ・スピードなどもそれぞれのパーティクルで別々の動きをするように実装します。

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を使ってどんな実装をしていこうかしら。
では、また〜

13
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?