There are two main things you're overlooking:
- as previously mentioned in the other question, you need to read/experiment/get an understanding of scope in JavaScript in general but also in React
- In this section:
onChange={(e) => setSelectedFile(e.target.files[0])}, files[0] points to data about the selected file (e.g. filename, how large is it (in bytes), etc., but it's not the actual file. const url = URL.createObjectURL(selectedFile); would simply point to the stackblitz url where your project lives and passing that loadImage() will result in a 1x1 pixel image. You need to use FileReader to load bytes from files[0]
- The point of the console log message hints in my previous comments were to help you debug and figure out where the "small bug" is. e.g. if how you expect the code to run is how it actually runs (otherwise test/correct assumptions/fix code/etc.).
Here's a tweaked version of your code:
import React, { useState } from 'react';
import Sketch from 'react-p5';
import './style.css';
export default function App() {
let backgroundImage;
let dragging = false; // Is the object being dragged?
let rollover = false; // Is the mouse over the ellipse?
let x, y, w, h; // Location and size
let offsetX, offsetY; // Mouseclick offset
let theP5Reference;
const setup = (p5, parentRef) => {
p5.createCanvas(1000, 500).parent(parentRef);
// Starting location
x = 350;
y = 50;
const url =
'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Image_created_with_a_mobile_phone.png/640px-Image_created_with_a_mobile_phone.png';
backgroundImage = p5.loadImage(url);
// Dimensions
w = 700;
h = 700;
// keep a reference to p5 for reuse outside of this function
theP5Reference = p5;
};
const draw = (p5) => {
p5.background(233);
if (
p5.mouseX > x &&
p5.mouseX < x + w &&
p5.mouseY > y &&
p5.mouseY < y + h
) {
rollover = true;
} else {
rollover = false;
}
// Adjust location if being dragged
if (dragging) {
x = p5.mouseX + offsetX;
y = p5.mouseY + offsetY;
}
p5.image(backgroundImage, x, y);
drawMaskOverlay(p5);
};
const drawMaskOverlay = (p5) => {
p5.fill(255);
p5.noStroke();
p5.beginShape();
// CW vertex winding
p5.vertex(0, 0);
p5.vertex(p5.width, 0);
p5.vertex(p5.width, p5.height);
p5.vertex(0, p5.height);
// cutout contour CCW
p5.beginContour();
p5.vertex(400, 100);
p5.vertex(400, 400);
p5.vertex(600, 400);
p5.vertex(600, 100);
p5.endContour();
p5.endShape();
};
const mousePressed = (p5) => {
if (
p5.mouseX > x &&
p5.mouseX < x + w &&
p5.mouseY > y &&
p5.mouseY < y + h
) {
dragging = true;
offsetX = x - p5.mouseX;
offsetY = y - p5.mouseY;
}
};
const mouseReleased = (p5) => {
// Quit dragging
dragging = false;
};
const onFileSelected = (e) => {
const files = e.target.files;
if (!files || !files[0]) {
console.log('no files selecting, early exit');
return;
}
// create a file reader to load the image (and get base64 data)
const reader = new FileReader();
reader.addEventListener('load', function (evt) {
// event.target.result is the base64 image
// use the references to p5 from setup()
backgroundImage = theP5Reference.loadImage(evt.target.result);
});
// trigger the load / base64 conversion (when ready will trigger the event handler above)
reader.readAsDataURL(files[0]);
};
return (
<div className="App">
<div>
<h1>Select an image</h1>
<input type="file" name="file" id="file" onChange={onFileSelected} />
<Sketch
setup={setup}
draw={draw}
mouseReleased={mouseReleased}
mousePressed={mousePressed}
/>
</div>
</div>
);
}
The key elements are these:
react-p5 gives you a reference to p5 (the general library, not your actual sketch) in methods such as setup() (which is a good candidate as it triggers only once, at the start). You can keep a reference to p5 in a separate variable in the outer scope (so it's visible from outside setup()) (theP5Reference in the snippet above).
- the file
<input> change handler (onFileSelected) is not inlined, as within the App's scope.
- In
onFileSelected a FileReader is instantiated, the load is triggered via readAsDataURL and in the load handler, the previously stored theP5Reference reference to p5 is reused to simply call loadImage() on. Because the handler is within App's scope, backgroundImage is already visible.
As an excercise, I suggest you read more react specific scope, look at react-p5's source code and see if you can make use of this.sketch / props, etc. to access p5 from App without keeping storing a reference from setup().