![見出し画像](https://assets.st-note.com/production/uploads/images/74920508/rectangle_large_type_2_683c9e5f8a063bb73ca4e24e4b65299b.jpeg?width=1200)
p5jsとReactの連携にReduxを使う
p5jsという、Cavas上での図形描画に特化したライブラリがあります。
コイツとReactをうまく連携させることができないか考えたところ、Reduxが便利じゃね?と思ったのでサンプルスクリプトを作ってみました。
サンプル
サンプルの内容
画面の上半分がp5js、下半分がReactコンポーネントになっています。
まず、マウスの座標をp5jsのキャンバス上で動かすと、円の輝度を変更でき、輝度の値がReactコンポーネント上にも示されます。
この輝度の値はReduxで管理しています。
![](https://assets.st-note.com/img/1648091186893-BkLhffd2IY.png)
次に、[小さくする]ボタンや[大きくする]ボタンを押すと、円の大きさを変えることができます。
この円の大きさの値もReduxで管理しています。
![](https://assets.st-note.com/img/1648091280130-fDHDLhWpdk.png)
状態をReduxで管理する
![](https://assets.st-note.com/img/1648091477163-zv0OkxjYDZ.jpg?width=1200)
p5jsの中からは、ReduxのStoreの中身を
Selector(store.getState()) で読み込み
store.dispatch(Action) で書き込む
ように、storeのメソッドを直接呼び出せばいいのです。
Hookを使わずに値を読み込んでしまうのはちょっとアウトローな感じかもしれませんが、こう書くのが一番楽でした~
Storeを作成
Redux Toolkitでサッと作ってしまいます。
//circleSlice.js
import { createSlice, createSelector } from "@reduxjs/toolkit";
import rgb from "hsv-rgb";
const initialState = {
size: 100,
brightness: 0.8
};
const circleSlice = createSlice({
name: "circle",
initialState,
reducers: {
increaseSize: (state) => {
state.size = state.size * 1.1;
},
reduceSize: (state) => {
state.size = state.size * 0.9;
},
setBrightness: (state, { payload }) => {
state.brightness = payload;
}
}
});
export const { increaseSize, reduceSize, setBrightness } = circleSlice.actions;
export const selectColor = createSelector(
(state) => state.circle,
(circle) => rgb(342, 85, 255 * circle.brightness)
);
export default circleSlice.reducer;
//store.js
import { configureStore } from "@reduxjs/toolkit";
import circleReducer from "./circleSlice";
export default configureStore({
reducer: {
circle: circleReducer
}
});
p5js
p5jsをReactに持ってくる際は、react-p5-wrapperを使います。
storeのメソッドを直接呼び出して、読み込みや書き込みを行います。
//P5View.js
import { P5Instance, ReactP5Wrapper } from "react-p5-wrapper";
import store from "./store";
import { selectColor, setBrightness } from "./circleSlice";
/**
*
* @param {P5Instance} _
*/
const sketch = (_) => {
_.setup = () => {
_.createCanvas(200, 200);
_.noStroke();
_.frameRate(5);
};
_.draw = () => {
_.background("grey");
const color = selectColor(store.getState()); #読み込み
_.fill(color);
const size = store.getState().circle.size; #読み込み
_.circle(100, 100, size);
const brightness = _.map(_.mouseX, 0, _.width, 0, 1, true);
store.dispatch(setBrightness(brightness)); #書き込み
};
};
export default () => <ReactP5Wrapper sketch={sketch} />;
一応、ReactP5Wrapperコンポーネントからsketch関数のスコープ内にpropsを引き込む方法はあるのですが、変数が多くなってごちゃごちゃして大変だったので特にpropsは何も渡していません。
普通のReactコンポーネント
普通のReactコンポーネントについては普通に正攻法でReduxを使います。
//ReactComponentView.js
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { increaseSize, reduceSize } from "./circleSlice";
export default () => {
const dispatch = useDispatch();
const brightness = useSelector((state) => state.circle.brightness);
return (
<div>
<button onClick={() => dispatch(reduceSize())}>小さくする</button>
<button onClick={() => dispatch(increaseSize())}>大きくする</button>
<p>輝度:{brightness}</p>
</div>
);
};
完成
完成した2つのReactコンポーネントを並べて完成です。
import { Provider } from "react-redux";
import store from "./store";
import P5View from "./P5View";
import ReactComponentView from "./ReactComponentView";
export default function App() {
return (
<Provider store={store}>
<P5View />
<ReactComponentView />
</Provider>
);
}