p5.jsを本番サイトで使う方法では、グローバルスコープ内(windowオブジェクト直下)に直接p5.jsを読み込ませ、p5.jsのグローバルモードとしてp5jsを実行する方法を解説しました。
この記事では、p5jsのインスタンスモードを使用し、Reactなどのコンポーネントとしてp5.jsを読み込ませる方法を解説します。
インスタンスモードとグローバルモードの違いはp5.jsを本番サイトで使う方法をご覧ください。
動くサンプルコードはCodeSandBoxにあります。コードのみ見たい方はそちらをご覧ください。
まずreactとp5をインストールしましょう。
関数コンポーネントで書きたいので、ReactのuseEffect
, useState
あたりを使いたいので、Reactのバージョンは16.8
以上が前提です。クラスコンポーネントで書きたい方は適宜置き換えてください。
npm install react react-dom p5 --save
次にWrappingコンポーネントを作成してきます。
P5Wrapper
という名前のReactの関数コンポーネントを作成します。
同時に、p5jsからcanvas要素を生成する先のdiv要素をwrapper
という参照名で作成します。
const P5Wrapper = props => {
const wrapper = React.createRef();
return <div ref={wrapper} />;
}
p5jsは圧縮版を_p5
として読み込ませます。
import _p5 from "p5/lib/p5.min";
クラスコンポーネントのStateとComponentDidMountに変わる関数を使いたいので、依存モジュールをimportします。
useStateがStateを扱う役割をし、useEffectがComponentDidMountの役割をします。
import React, { useState, useEffect } from "react";
p5のインスタンス格納用に、stateを宣言します。
ここでp5という名前を使いたかったので、p5js本体は_p5
という名前にしています。
const const [p5, setP5] = useState(0);
useEffectを使い、p5jsをwrapper
にマウントさせます。
wrapperはdiv要素なので、div要素がレンダリングされたあと実行されるuseEffect
の中でないと、p5jsをマウントできません。
第2引数にprops.sketch
を渡さないと無限にcanvasが生成されてしまいますのでご注意ください。
useEffect(() => {
setP5(new _p5(props.sketch, wrapper.current));
}, [props.sketch]);
全体のコードは以下のようになります。
これでp5jsのWrapperコンポーネントが完成しました。
import React, { useState, useEffect } from "react";
import _p5 from "p5/lib/p5.min";
import PropTypes from "prop-types";
const P5Wrapper = props => {
const [p5, setP5] = useState(0);
const wrapper = React.createRef();
useEffect(() => {
setP5(new _p5(props.sketch, wrapper.current));
}, [props.sketch]);
return <div ref={wrapper} />;
};
P5Wrapper.propTypes = {
sketch: PropTypes.func
};
export default P5Wrapper;
つぎにsketchファイルを別ファイルとして用意しておきます。
内容は300x300pxのcanvas内でマウス座標に従い赤い丸が描画されるというものです。
const sketch = p => {
p.setup = () => {
p.createCanvas(300, 300);
};
p.draw = () => {
p.background(240);
if (p.mouseX === 0 && p.mouseY === 0) return;
p.fill(255, 0, 0);
p.noStroke();
p.ellipse(p.mouseX, p.mouseY, 100, 100);
};
};
export default sketch;
上記で作成したP5Wrapper.jsとsketch1.jsファイルを使用し。 Reactのindexファイルから参照します。
import React from "react";
import ReactDOM from "react-dom";
import P5Wrapper from "./P5Wrapper";
import sketch1 from "./sketches/sketch1";
function App() {
return (
<div className="App">
<h1>Hello p5.js in React</h1>
<P5Wrapper sketch={sketch1} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
以上で完了です。
動くサンプルコードはCodeSandBoxにありますのでご覧ください。
上でp5jsのwrappingコンポーネントを自作しましたが、もっとお手軽に使いたい方は、同様の機能が実装されているreact-p5-wrapperを使うのがおすすめです。
コードはGithubのREADMEより拝借いたしました。
内容も同じなので、そちらを見ていただいても同じです。
それではまず、既存プロジェクトに依存モジュールをインストールします。
npm install react-p5-wrapper --save
まずsketchファイルは1つのJSファイルとして完結させます。
グローバルモードから移植する場合は、すべてのp5js関数の先頭にp.
をつけるだけです。
Reactのアプリケーションスコープから値もpropsとして渡せます。
myCustomRedrawAccordingToNewPropsHandler
関数で値を受け取り、内部スコープの変数に格納しています。
export default function sketch (p) {
let rotation = 0;
p.setup = function () {
p.createCanvas(600, 400, p.WEBGL);
};
p.myCustomRedrawAccordingToNewPropsHandler = function (props) {
if (props.rotation !== null){
rotation = props.rotation * Math.PI / 180;
}
};
p.draw = function () {
p.background(100);
p.normalMaterial();
p.noStroke();
p.push();
p.rotateY(rotation);
p.box(100);
p.pop();
};
};
あとはreact-p5-wrapperコンポーネントのpropsに上で作成したsketch.jsファイルを渡します。
import P5Wrapper from 'react-p5-wrapper';
import sketch from './sketch.js';
<P5Wrapper sketch={sketch} />
P5Wrapper
コンポーネント内で自動でcanvasが生成されp5jsが実行されます。
以上でライブラリ版は完了です。
react-p5-wrapperを使った例も書きましたが、自分でいろいろカスタマイズしたい場合は、冒頭のカスタムコンポーネントを作成することをおすすめします。
Vueのコンポーネントは書いていませんが、考え方はReactと同じです。
他にもiframeで動かすケースも考えられますが、iframeの参照URL先でグローバルモードのp5jsを動かすだけですので省略します。
後日余力があれば、nodejsとかと組み合わせて書いていきたいと思います。
それでは、よいp5ライフを!