I've created a canvas which is set to a state using a callback. circles are then created based on mouse x and y that are then drawn to the canvas state after clearing the canvas for each frame. each draw frame reduces the radius of the circles until they fade away
Currently the canvas only updates inside of canvasDraw() on mousemove.
The problem: the canvas needs to be updated using an interval inisde of the useEffect() method so that the circles gradually reduce in radius over time.
for some reason retrieving the canvas state inside of useEffect() returns null. I think this may be becuase the useEffect interval is called once but before the ctx is able to be initialised to a state. But I dont know where to go from here...
The following link was useful in fixing some leaks in my code but still results in a empty state at useEffect(): Is it safe to use ref.current as useEffect's dependency when ref points to a DOM element?
import React, { useEffect, useState, useCallback, useReducer } from "react";
const Canvas = () => {
const [isMounted, toggle] = useReducer((p) => !p, true);
const [canvasRef, setCanvasRef] = useState();
const [ctx, setCtx] = useState();
const handleCanvas = useCallback((node) => {
setCanvasRef(node);
setCtx(node?.getContext("2d"));
}, []);
const [xy, setxy] = useState(0);
const [canDraw, setCanDraw] = useState(false);
const [bubbles, setBubbles] = useState([]);
const canvasDraw = (e) => {
setxy(e.nativeEvent.offsetX * e.nativeEvent.offsetY);
xy % 10 == 0 ? setCanDraw(true): setCanDraw(false);
canDraw && createBubble(e.nativeEvent.offsetX, e.nativeEvent.offsetY);
drawBubbles();
};
const createBubble = (x, y) => {
const bubble = {
x: x,
y: y,
radius: 10 + (Math.random() * (100 - 10))
};
setBubbles(bubbles => [...bubbles, bubble])
}
const drawBubbles = useCallback(() => {
if (ctx != null){
ctx.clearRect(0,0,canvasRef.width,canvasRef.height);
bubbles.forEach((bubble) => {
bubble.radius = Math.max(0, bubble.radius - (0.01 * bubble.radius));
ctx.beginPath()
ctx.arc(bubble.x, bubble.y, bubble.radius, 0, 2 * Math.PI, false)
ctx.fillStyle = "#B4E4FF"
ctx.fill()
}, [])
}
});
useEffect(() => {
const interval = setInterval(() => {
console.log(ctx);
drawBubbles(ctx); // <------ THE PROBLEM (ctx is null)
}, 100)
return () => clearInterval(interval);
}, []);
return (
<main className="pagecontainer">
<section className="page">
<h1>Bubbles!</h1>
{isMounted && <canvas
onMouseMove={canvasDraw}
ref={handleCanvas}
width={`1280px`}
height={`720px`}
/>}
</section>
</main>
);
}
export default Canvas