I am trying to implement the code below in my react app. And I am not sure how to deal(?) with class Walker
.
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com
let walker;
function setup() {
createCanvas(640,360);
walker = new Walker();
background(127);
}
function draw() {
walker.step();
walker.render();
}
class Walker {
constructor(){
this.x = width/2;
this.y = height/2;
}
render() {
stroke(0);
point(this.x,this.y);
}
step() {
var choice = floor(random(4));
if (choice === 0) {
this.x++;
} else if (choice == 1) {
this.x--;
} else if (choice == 2) {
this.y++;
} else {
this.y--;
}
this.x = constrain(this.x,0,width-1);
this.y = constrain(this.y,0,height-1);
}
}
Currently I wrote the code as below:
import React, { useRef, useEffect } from "react";
import p5 from "p5";
let width;
let height;
class Walker {
constructor(p5) {
this.x = width / 2;
this.y = height / 2;
}
render(p5) {
p5.stroke(0);
p5.point(this.x, this.y);
}
step(p5) {
let choice = Math.floor(Math.random(4));
if (choice === 0) {
this.x++;
} else if (choice === 1) {
this.x--;
} else if (choice === 2) {
this.y++;
} else {
this.y--;
}
this.x = p5.constrain(this.x, 0, width - 1);
this.y = p5.constrain(this.y, 0, height - 1);
}
}
const App = () => {
const processingRef = useRef();
let walker;
const Sketch = (p) => {
p.setup = () => {
p.createCanvas(640, 360);
walker = new Walker(p5);
p.background(127);
};
p.draw = () => {
walker.step();
walker.render();
};
};
useEffect(() => {
new p5(Sketch, processingRef.current);
}, []);
return <div ref={processingRef} />;
};
export default App;
I got TypeError: Cannot read property 'constrain' of undefined
. I would like to know how to fix this problem!
I cannot comment on whether this pattern for using p5.js with ReactJS is a good approach, however, it looks like there is an issue with your conversion to instance mode:
class Walker {
// I think you want to assign p5 (possibly rename to p for consistency)
// to be an instance property (i.e. this.p = p)
constructor(p5) {
// You declare width and height above, but they're never assigned.
// You should use this.p.width and this.p.height instead
this.x = width / 2;
this.y = height / 2;
// i.e.: this.p = p5;
}
// Get rid of the p5 argument and use the instance property this.p
render(p5) {
// i.e. Change this:
p5.stroke(0);
// to this:
// this.p.stroke(0);
// ...
}
step() {
// Math.random does not work like the p5.js random function, it does not take
// parameters. You'll either need to switch this to this.p.random or use
// Math.random() * 4
let choice = Math.floor(Math.random(4));
// ...
}
}
const App = () => {
// ...
const Sketch = (p) => {
p.setup = () => {
p.createCanvas(640, 360);
// I think you mean to be passing the p instance to the Walker constructor,
// not the p5 namespace object (which will only contain classes)
walker = new Walker(p5);
p.background(127);
};
// ...
};
// ...
};
And, because I was curious about embedding simple ReactJS apps as Stack Snippets, here's a working version:
class Walker {
constructor(p) {
this.x = p.width / 2;
this.y = p.height / 2;
this.p = p;
}
render() {
this.p.stroke(0);
this.p.point(this.x, this.y);
}
step() {
let choice = Math.floor(this.p.random(4));
if (choice === 0) {
this.x++;
} else if (choice === 1) {
this.x--;
} else if (choice === 2) {
this.y++;
} else {
this.y--;
}
this.x = this.p.constrain(this.x, 0, this.p.width - 1);
this.y = this.p.constrain(this.y, 0, this.p.height - 1);
}
}
const Sketch = (p) => {
let walker;
p.setup = () => {
p.createCanvas(640, 360);
walker = new Walker(p);
p.background(127);
};
p.draw = () => {
walker.step();
walker.render();
};
};
// Switching to class style component because useEffect is only
// available in ReactJS >= 16.8.x and Stack Snippets don't have
// support yet
class App extends React.Component {
constructor() {
super();
// Switching to React.createRef
this.processingRef = React.createRef();
}
componentDidMount() {
new p5(Sketch, this.processingRef.current);
}
componentDidUpdate() {
// TODO: cleanup old sketches
new p5(Sketch, this.processingRef.current);
}
render() {
return <div ref={this.processingRef} />;
}
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>