Hi! So I spent a couple of days trying to figure out how to use <canvas>
in React.
Canvas lets you output graphics on the screen
You may use it for games like Pong or data visualization
As a result I put together a very basic app to use it as a reference in case I need it in a project. Let me go through the code briefly!
What you see on the screen
Basically the app is supposed to do three things
- Output graphics on the screen
- Define the movement of the box
- Let you pause / resume the animation with a button
What you see in the code
Here is the source code. And the working app.
Essentially the application has three parts.
const CanvasPractice = () => {
// get canvas
const canvasRef = useRef()
// set frame counter
const [counter, setCounter] = useState(0)
const [shouldStop, setShouldStop] = useState(true)
// box position and speed
const [positionX, setPositionX] = useState(165)
const [positionY, setPositionY] = useState(165)
const [dx, setDx] = useState(2)
const [dy, setDy] = useState(1.5)
const [motionType, setMotionType] = useState('Circle')
// update the counter
useLayoutEffect(() => {
if (!shouldStop) {
let timerId
const animate = () => {
setCounter(c => c + 1)
timerId = requestAnimationFrame(animate)
}
timerId = requestAnimationFrame(animate)
return () => cancelAnimationFrame(timerId)
}
}, [shouldStop])
// output graphics
useEffect(() => {
const canvas = canvasRef.current
const context = canvas.getContext('2d')
context.clearRect(0, 0, 350, 350)
// some code to calculate position
context.fillStyle = '#555555'
context.fillRect(positionX, positionY, 20, 20)
}, [counter])
const changeMotionType = () => {
if (motionType === 'Circle') {
setMotionType('Bounce')
} else {
setMotionType('Circle')
}
}
return (
<div className='container'>
<canvas ref={canvasRef}
width="350px" height="350px"
onClick={changeMotionType}
/>
<h3>Frame count: {counter}</h3>
<p>Motion type is {motionType}</p>
<button
onClick={() => setShouldStop(!shouldStop)}>
{ shouldStop ? 'Start' : 'Stop' }
</button>
</div>
)
}
export default CanvasPractice
1. The Engine
useLayoutEffect()
section serves as an engine. requestAnimationFrame()
function refreshes itself roughly 60 times a second and increased the counter value. It's the main pulse of the app.
You feed the counter as a dependency to the useEffect()
section causing it to refresh and update the graphics on the screen.
Further reading
Here is a great article explaining how requestAnimationFrame()
function works
Using requestAnimationFrame with React Hooks
And here you may find superuseful notes on why you might prefer useLayoutEffect() over useEffect() for this application
requestAnimationFrame and useEffect vs useLayoutEffect
2. Output
In the useEffect() section you initialize the canvas.
Calculate box position for the current frame, updating positionX
and positionY
state.
And output it using the context.fillRect(positionX, positionY, 20, 20)
method.
3. In control
The button lets you pause / resume the animation.
Then there are two modes in which the box moves. It runs in circles or bounces off the sides of the board, like it would do in a video game. You can switch the mode by clicking anywhere on the board.
So
Did you find this exploration interesting?
If you have any suggestions, let me know!
Update
And here is the actual game built on the same foundation
poung.ptifur.digital
Top comments (0)