パーティクルに興味をもったので色々調べて作ってみた。
PixiJSのParticle-Containerを試してみたが、パーティクルの操作にかなり縛りがあったので使用せず。
また、この記事のコードはICSさんの下記のURLを参考にPixiJSを使ってコーディングしました(要PixiJSだったため)
JavaScriptとWebGLで取り組む
クリエイティブコーディング
https://www.slideshare.net/clockmaker_jp/javascriptwebgl-107429033
テスト的にやってみただけ&パーティクルを出したり引っ込めたかったのでオブジェクトプーリングはしてません。
追々対応予定。
作ったばかりでコードを整理していないので、微妙なとこがあるかもしれません(特に命名規則
import * as PIXI from 'pixi.js'
export class ParticleStage extends Dispatcher {
private app
private container
private particles = []
// 1フレームごとに生成するパーティクルの数
private totalSpritesPerTick = 4
// パーティクルのライフ
private MAX_LIFE = 100
public constructor(){
super()
this.app = new PIXI.Application({
resolution: devicePixelRatio,
transparent: true,
})
// パーティクルを追加していくコンテナインスタンスを作成
this.container = new PIXI.Container()
// ステージに追加
this.app.stage.addChild(this.container)
return this.app
}
// パーティクルを開始するメソッド
public startTick() {
//パーティクル作成tickを登録
this.app.ticker.add(this.createTick, this)
this.app.ticker.start()
}
// パーティクルを停止するメソッド
public stopTick() {
this.app.ticker.remove(this.createTick, this)
}
// 1フレーム毎に呼び出されるパーティクルを生成するメソッド
private createTick() {
for (let i = 0; i < this.totalSpritesPerTick; i++) {
// PIXI.Spriteを拡張したパーティクルインスタンスを作成
const particle = new Particle()
// なんとなくぼかす
particle.filters = [new PIXI.filters.BlurFilter(5)]
// パーティクルの透明度をランダムで設定
particle.alpha = Math.random() * 0.2 + 0.5
// パーティクルの中心を設定
particle.anchor.set(0.5)
// 浮き上がる速度を設定
particle.vy = 20 * (Math.random() - 0.5)
// パーティクルのライフを設定
particle.life = this.MAX_LIFE
// パーティクルのシェイプを決定
particle.type = Math.floor(Math.random() * 2).toString()
// パーティクルのシェイプをtypeごとに生成
const g = new PIXI.Graphics()
switch (particle.type) {
case '0':
g.beginFill(0xFFFFFF)
g.drawCircle(0, 0, 15)
g.endFill()
break
case '1':
g.lineStyle(3, 0xFFFFFF)
g.drawCircle(0, 0, 15)
break
}
g.endFill()
// GraphicsをTextureに変換
const t = this.app.renderer.generateTexture(g)
particle.texture = t
// パーティクルの出現位置を設定(今回はnormal)
const randomValue = this.createRandom('normal')
particle.x = window.innerWidth * (randomValue)
particle.y = window.innerHeight + 10
// パーティクルを配列に保存
this.particles.push(particle)
// コンテナにaddChild
this.container.addChild(particle)
}
}
// パーティクルを動かすメソッド
private tick() {
for (let i = 0; i < this.particles.length; i++) {
// オブジェクトの作成
const particle = this.particles[i]
// 重力
particle.vy -= 2
// 摩擦
particle.vy *= 0.92
// 速度を位置に適用
particle.y += particle.vy
// パーティクルのサイズをライフ依存にする
const scale = particle.life / this.MAX_LIFE;
particle.scale.x = scale
particle.scale.y = scale
// パーティクルの透過度をライフ依存にする(0.6が下限)
particle.alpha = (1-scale) + 0.6
// 寿命を減らす
particle.life -= 1;
// 寿命の判定
if (particle.life <= 0) {
// 変数を初期化
particle.reset()
// コンテナから削除
this.container.removeChild(particle)
// 配列からも削除
this.particles.splice(i, 1)
i -= 1
}
}
}
private createRandom(randomType) {
switch (randomType) {
case 'center':
// 中央に配置
return (Math.random() + Math.random()) / 2
case 'center-strong':
// さらに中央に配置
const valueA = (Math.random() + Math.random()) / 2
const valueB = (Math.random() + Math.random()) / 2
return (valueA + valueB) / 2
case 'edge':
// 乗算の乱数
const base = Math.random() * Math.random() * Math.random()
const inverse = 1.0 - base
return Math.random() < 0.5 ? base : inverse
case 'normal':
default:
// 一様分布
return Math.random()
}
}
}
export class Particle extends PIXI.Sprite {
public vx = 0
public vy = 0
public life = 0
public size = 0
public type
constructor() {
super()
}
public reset() {
this.vx = 0
this.vy = 0
this.life = 0
this.size = 0
this.type = 0
this.texture = null
this.destroy()
}
}
試してみる
import { ParticleStage } from './ParticleStage '
const particles = new ParticleStage()
// 適当なdivのクラス名
const div = document.getElementsByClassName('canvas-stage')
// canvasを追加
div.appendChild(particles.view)
//パーティクルを開始
particles.startTick()
// 1秒後に停止
setTimeout(particles.stopTick(), 1000)
実際のものはHSLでランダムに色を変更したりしています。
パーティクルおもしろいなぁ。