将 p5.js 与 React 一起使用时如何声明 class

How to declare class when using p5.js with react

我正在尝试在我的 React 应用程序中实现以下代码。而且我不确定如何处理(?)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);
  }
}

目前我写的代码如下:

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;


我得到了TypeError: Cannot read property 'constrain' of undefined。我想知道如何解决这个问题!

我无法评论这种将 p5.js 与 ReactJS 结合使用的模式是否是一个好方法,但是,您转换为实例模式似乎存在问题:

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);
    };

    // ...
  };
  // ...
};

而且,因为我对将简单的 ReactJS 应用嵌入为 Stack Snippets 感到好奇,这里是一个工作版本:

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>