Search code examples
reactjsp5.jspreload

I have problem using preload function to load images with react-p5 library


I have used react-p5 library to integrate the P5 library with React but when I use the preload function to load images it gives me and error. The question is, how do I get the preload p5 function to work with loading images in React?

Here is the code:

import React, { Component } from "react";
import Sketch from "react-p5";
import ReactJS from './images/Icons/React.jpg';
let test;
export default class Sket extends Component {
  x = 50
  y = 50

  preload = (p5) =>{
     test = p5.loadImage(ReactJS, ()=>{
        p5.image(test, 0, 0, 50, 50)
     });
  }

  setup = (p5, parent) => { // window width is still not optimized for extra large screen
    var sket = p5.createCanvas(parent.offsetWidth, parent.offsetWidth).parent(parent);
    sket.position(0, 0);
  }
  draw = (p5) => {
    p5.background(0)
    p5.ellipse(this.x, this.y, 70, 70)
    this.x++
    this.y++
  //  p5.image(test, 50, 50)
  }

  render() {
    return <Sketch setup={this.setup} draw={this.draw} />
  }
}

Solution

  • Load your image from URL, not "from a variable", so:

    export default class Sketch extends Component {
      constructor(props) {
        super(props);
        // just keep these in your constructor:
        this.x = 50;
        this.y = 50;
      } 
    
      setup(p5, parent) {
        this.p5 = p5;
        p5.loadImage("./images/blah.jpg", img => {
          this.img = img;
          p5.redraw(); // <- only if you're running with noLoop()
        });
      }
    
      ...
    

    and then with draw updated to draw your image only if and when it exists:

    draw() {
      const { p5, img, x, y } = this;
    
      if (img) {
        p5.image(img, x, y);
      }
    
      ...
    }
    

    And of course: you can't just say <Sketch setup={this.setup} draw={this.draw}> because the this keyword will be pointing at the global scope by the time that runs. Ever since React 16, you either need to use bind in the constructor to explicitly shadow your functions (which is a terrible idea), or use arrow functions to make sure that this points to what it is at declare time.

    Arrow functions are by far the better choice:

    render() {
      return <Sketch
        setup={(...args) => this.setup(...args)}
        draw={() => this.draw()}
      />;
    }