I'm playing around with React and Canvas. I wanna animate the movement of a square through requestAnimationFrame. So I made the main component to handle canvas element and update of square's position:
function App() {
let canvas = useRef()
let square = useSquare(WIDTH / 2, HEIGHT, 2)
let update = () => {
square.update()
requestAnimationFrame(update)
}
useEffect(update, [])
return <>
<canvas ref={canvas} width={WIDTH} height={HEIGHT} />
<Renderer.Provider value={() => ref.canvas.getContext("2d")}>
<Square {...square} />
</Renderer.Provider>
</>
}
The Square is a component but it doesn't return JSX. It runs drawing oprations at useEffect:
function Square({ x, y }) {
let getContext = useContext(Renderer)
useEffect(() => {
let ctx = getContext()
ctx.fillStyle = "#000"
ctx.fillRect(x, y, 10, 10)
}, [x, y])
return null;
}
And I wanna use a hook to encapsulate state and behavior of the square:
function useSquare(init_x, init_y) {
let [x, set_x] = useState(init_x)
let [y, set_y] = useState(init_y)
let [vx, set_vx] = useState(10)
let [vy, set_vy] = useState(-5)
let update = () => {
set_x(prev_x => prev_x + vx)
set_y(prev_y => prev_y + vy)
if (x <= 0 || x >= WIDTH) set_vx(prev_vx => -prev_vx)
else if (y <= 0 || y >= HEIGHT) set_vy(prev_vy => -prev_vy)
}
return { update, x, y }
}
Seems reasonable to me. However, it doesn't work as expected. Square is moving, but it doesn't respond to collisions with the viewport borders.
If I put console.log(x, y) into either of update functions, I can see it fires with the expected rate, but the state isn't changing.
I don't understand why the component is able to render correctly if the state isn't changing. Frankly, I have no idea what I'm doing wrong. Perhaps, someone helps me to clarify. Thanks in advance.
PS: I made a codesanbox with the example https://codesandbox.io/s/xenodochial-jepsen-nfxv6