bauhausify美大出身の
WEBエンジニアリング奮闘記
CODING

p5.jsをReactのコンポーネントとして実行する方法

2019.09.17
p5.jsを直接ページ内に読み込ませるのではなく、Reactなどのコンポーネントの中で実行する方法をコードベースでご紹介します。

p5.jsを本番サイトで使う方法では、グローバルスコープ内(windowオブジェクト直下)に直接p5.jsを読み込ませ、p5.jsのグローバルモードとしてp5jsを実行する方法を解説しました。

この記事では、p5jsのインスタンスモードを使用し、Reactなどのコンポーネントとしてp5.jsを読み込ませる方法を解説します。
インスタンスモードとグローバルモードの違いはp5.jsを本番サイトで使う方法をご覧ください。

p5.jsのWrappingコンポーネントをゼロから作る

動くサンプルコードは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コンポーネントが完成しました。

P5Wrapper.js
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内でマウス座標に従い赤い丸が描画されるというものです。

sketches/sketch1.js
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にありますのでご覧ください。

[おまけ]react-p5-wrapperライブラリ編

上でp5jsのwrappingコンポーネントを自作しましたが、もっとお手軽に使いたい方は、同様の機能が実装されているreact-p5-wrapperを使うのがおすすめです。
コードはGithubのREADMEより拝借いたしました。
内容も同じなので、そちらを見ていただいても同じです。

それではまず、既存プロジェクトに依存モジュールをインストールします。

npm install react-p5-wrapper --save

まずsketchファイルは1つのJSファイルとして完結させます。
グローバルモードから移植する場合は、すべてのp5js関数の先頭にp.をつけるだけです。
Reactのアプリケーションスコープから値もpropsとして渡せます。
myCustomRedrawAccordingToNewPropsHandler関数で値を受け取り、内部スコープの変数に格納しています。

sketch.js
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ライフを!

この記事を共有しよう!

前の記事
p5.jsを本番サイトで使う方法
次の記事
2019年版Node-jsのおすすめインストール方法
関連記事
目次

エムロート [Mwrote]

多摩美術大学
プロダクトデザイン専攻卒業 ->
GUIデザイン会社勤務 ->
現在フロントエンジニア

My site ->
www.mwrote.com