Reputation: 99
I have been trying to create a little animation on canvas using React.js.
I expect the red block inside the canvas to go up and drop back down when I hit my spacebar by decreasing the velocity so that it will execute the if statement I have got and pull it back down, but so far it's not working. I can see the velocity value did change and it did execute the if statement when I console.log
, it doesn't seem like showing on the animation.
const { useRef, useEffect, useState } = React;
const Canvas = () => {
const { innerWidth: innerwidth, innerHeight: innerheight } = window;
const contextRef = useRef()
const [position, setPosition] = useState({ x: 100, y: 100 })
const [size, setSize] = useState({ width: 30, height: 30 })
const [velocity, setVelocity] = useState({ x: 0, y: 0 })
const gravity = 4.5
const draw = (context) => {
context.fillStyle = 'red'
context.fillRect(position.x, position.y, size.width, size.height);
};
const update = (context, canvas) => {
draw(context)
setPosition({ x:position.x, y:position.y += velocity.y })
if (position.y + size.height +velocity.y <= canvas.height) {
setVelocity({ x:velocity.x, y: velocity.y += gravity })
} else {
setVelocity({ x:velocity.x, y: velocity.y = 0 })
}
}
const animate = (context, width, height, canvas) => {
requestAnimationFrame(() => {
animate(context, width, height, canvas )
})
context.clearRect(0, 0, width, height)
update(context, canvas)
}
useEffect(() => {
const canvas = contextRef.current;
const context = canvas.getContext("2d");
canvas.width = innerwidth - 2
canvas.height = innerheight - 2
animate(context, canvas.width, canvas.height, canvas)
}, []);
const handleKeydown = (e) => {
switch(e.keyCode) {
case 37:
console.log(velocity)
return "left";
case 39:
return "right";
case 32:
setVelocity({ x:velocity.x, y:velocity.y -= 50 });
console.log(velocity)
break
default:
console.log("keyCode is " + e.keyCode)
return 'default';
}
}
return (
<canvas ref={contextRef} tabIndex={-1} onKeyDown={(e) => {
handleKeydown(e)
}} />
);
};
const App = () => <Canvas />;
ReactDOM
.createRoot(document.getElementById("root"))
.render(<App />);
html, body { width: 100%; height: 100%; margin: 0; }
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
What can I try next?
Upvotes: 2
Views: 91
Reputation: 48638
You should wrap some of your function in a useCallback
so that they are not redefined on each render.
Also, I would use key
instead of keyCode
.
const { useCallback, useEffect, useState, useRef } = React;
const gravity = 4.5;
const Canvas = () => {
const { innerWidth: innerwidth, innerHeight: innerheight } = window;
const canvasRef = useRef();
const [position, setPosition] = useState({ x: 100, y: 100 });
const [size, setSize] = useState({ width: 30, height: 30 });
const [velocity, setVelocity] = useState({ x: 0, y: 0 });
const draw = useCallback((context) => {
context.fillStyle = 'red';
context.fillRect(position.x, position.y, size.width, size.height);
}, [position, size]);
const update = useCallback((context, canvas) => {
draw(context)
setPosition({ x: position.x, y: position.y += velocity.y })
if (position.y + size.height + velocity.y <= canvas.height) {
setVelocity({ x: velocity.x, y: velocity.y += gravity })
} else {
setVelocity({ x:velocity.x, y: velocity.y = 0 })
}
}, [position, size, velocity]);
const animate = (context, width, height, canvas) => {
requestAnimationFrame(() => {
animate(context, width, height, canvas);
});
context.clearRect(0, 0, width, height);
update(context, canvas);
};
useEffect(() => {
const canvas = canvasRef.current;
const context = canvas.getContext('2d');
canvas.width= innerwidth - 10;
canvas.height= innerheight - 10;
animate(context, canvas.width, canvas.height, canvas)
}, []);
const handleKeyDown = useCallback(({ key }) => {
switch (key) {
case 'ArrowLeft':
break;
case 'ArrowRight':
break;
case ' ': // Spacebar
setVelocity({ x: velocity.x, y: velocity.y -= 50 });
console.log(velocity)
break
default:
console.log(`Unknown key: ${key}`);
}
}, []);
return (
<canvas
ref={canvasRef}
tabIndex={-1}
onKeyDown={handleKeyDown}
/>
);
};
const App = () => <Canvas />;
ReactDOM
.createRoot(document.getElementById("root"))
.render(<App />);
html, body, #root { width: 100%; height: 100%; margin: 0; }
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
Upvotes: 1