Pixi.js とは、ブラウザ上で動作するWebGLを用いた2Dグラフィックスライブラリです。
Pixi.js は、JavaScript から使用するライブラリで、以下のようなプログラムを書くことで使用できます。
const app = new PIXI.Application()
document.body.appendChild(app.view)
// スプライトを追加
const sprite = PIXI.Sprite.from("hoge.png")
app.stage.addChild(sprite)
上記の例では、Pixi.js の初期化と、スプライト(画像)の表示を行っています。PIXI.Sprite.from
で新たなスプライトのオブジェクトを生成し、それをapp.stage
に対してaddChild
で追加することで、画像の表示が行われる、という例です。
Pixi.js は、表示物の情報を階層構造で保持し、その情報に従って画面の表示を行います。これは、HTMLの構造とよく似ています。
そこで、Custom Element の機能を用いれば、HTML で Pixi.js の表示物を宣言的に定義して Pixi.js による画面表示が行えるのではないかと考えて、試してみました。
Custom Element を使った要素の定義
まずは、PIXI.Application
を指す<pixi-app>
という要素を定義してみます。とりあえず動作させられることだけを確認したいため、内部の実装は雑です。
customElements.define("pixi-app", class PixiApplication extends HTMLElement {
constructor() {
super()
this.pixiObj = new PIXI.Application()
const shadow = this.attachShadow({mode: "open"})
shadow.appendChild(this.pixiObj.view)
const style = document.createElement("style")
style.textContent = `
:host {
display: inline-block;
}
`
shadow.appendChild(style)
}
})
Shadow DOM で、内部に Pixi.js の<canvas>
要素を追加しています。
次に、画像リソースを表すPIXI.Texture
の要素として<pixi-texture>
を定義します。
customElements.define("pixi-texture", class PixiTexture extends HTMLElement {
constructor() {
super()
const pixiApp = getPixiApp(this)
if (pixiApp === null) {
return
}
const src = this.getAttribute("src")
this.pixiObj = new PIXI.Texture.from(src)
}
})
次に、画像の表示を行う要素であるPIXI.Sprite
の要素として<pixi-sprite>
を定義します。親要素から<pixi-app>
要素を探す関数としてgetPixiApp()
関数も定義して使用しています。
const getPixiApp = (elem) => {
let node = elem.parentElement
while (node !== null) {
if (node.tagName === "PIXI-APP") {
return node
}
node = node.parentElement
}
return null
}
customElements.define("pixi-sprite", class PixiSprite extends HTMLElement {
constructor() {
super()
const pixiApp = getPixiApp(this)
const pixiTexture = pixiApp.querySelector(`#${this.getAttribute("texture")}`)
this.pixiObj = new PIXI.Sprite(pixiTexture.pixiObj)
const parentPixiObj = this.parentElement.pixiObj
if (parentPixiObj instanceof PIXI.Application) {
parentPixiObj.stage.addChild(this.pixiObj)
} else if (parentPixiObj instanceof PIXI.Container) {
parentPixiObj.addChild(this.pixiObj)
}
}
})
<pixi-sprite>
のtexture
属性の値は、表示の対象としたい<pixi-texture>
要素のid
属性の値が設定される想定です。
これで、とりあえず何らかの表示を行うための最小限の要素の定義ができました。
実際に使用した例は以下のようになります。
<pixi-app>
<pixi-texture id="bunny" src="bunny.png"></pixi-texture>
<pixi-sprite texture="bunny"></pixi-sprite>
</pixi-app>
<pixi-app>
の子要素として<pixi-texture>
、<pixi-sprite>
を追加する形を想定しています。
とりあえず表示だけはされるのかの実験をするために、PIXI.Sprite
の各プロパティを操作するための仕組みの実装は省略しました。
定義した要素を使用する
動作を試しますと、以下のように黒い<canvas>
の左上に小さな画像が表示されます。
うまくできました。
この Pixi.js の要素を HTML の要素として記述できる仕組みが本格的に実装できれば(されれば)、React、Preact、Superfine などの仮想DOMの仕組みを持ったフレームワークを用いて Pixi.js プログラミングができそうです。
すでに React のコンポーネントで Pixi.js の要素を扱えるようにするライブラリとして React PIXI というものがありますが、こちらは React 限定になります。