React中Canvas仅在浏览器窗口Resize后才绘制第二个图形求助
React Canvas 绘制异常:初始红色矩形不显示,Resize后正常的问题分析与解决
问题原因
- useEffect依赖缺失:你的
useEffect未声明依赖数组,仅在组件首次渲染时执行。若enemy1X、enemy1Y等变量初始值异常(比如超出画布范围),或后续更新时useEffect未重新触发,drawFun会一直使用旧的变量值,导致红色矩形无法显示。窗口Resize时组件重新渲染,useEffect再次执行拿到正确值,矩形才显示。 - 闭包变量捕获问题:
drawFun定义在useEffect内部,它捕获的是useEffect执行时刻的变量快照,后续外部变量更新时,drawFun无法获取最新值。 - Canvas尺寸未正确初始化:代码中未体现
canvasWidth、canvasHeight及context的获取逻辑,若初始时Canvas元素尺寸未正确设置,红色矩形可能被绘制在画布可视区域外,Resize时尺寸更新才显现。
解决方案
1. 补全useEffect依赖数组
将drawFun用到的所有外部变量加入依赖数组,确保变量更新时useEffect重新执行,更新drawFun的变量引用:
const Canvas = props => { useEffect(() =>{ const player1 = { x: playerX, y: playerY, draggable: gameStatus } const enemy1 = { x: enemy1X, y: enemy1Y, width: enemy1Width, height: enemy1Height} const drawFun = () => { context.clearRect(0, 0, canvasWidth, canvasHeight) context.fillStyle = '#00f4cc' context.fillRect(player1.x, player1.y, playerWidth, playerHeight) context.fillStyle = 'red' context.fillRect(enemy1.x, enemy1.y, enemy1.width, enemy1.height) requestAnimationFrame(drawFun); } const animationId = drawFun(); // 组件卸载或依赖变化时取消动画帧,避免内存泄漏 return () => cancelAnimationFrame(animationId); }, [playerX, playerY, gameStatus, enemy1X, enemy1Y, enemy1Width, enemy1Height, canvasWidth, canvasHeight, context]); }
注:
beginPath()对于填充矩形不是必需的,可以省略以简化代码。
2. 确保Canvas上下文与尺寸正确获取
使用useRef获取Canvas元素,直接从元素上获取上下文和实际尺寸,避免尺寸不匹配:
import { useRef, useEffect } from 'react'; const Canvas = props => { const canvasRef = useRef(null); useEffect(() =>{ const canvas = canvasRef.current; if (!canvas) return; const context = canvas.getContext('2d'); const canvasWidth = canvas.width; const canvasHeight = canvas.height; const drawFun = () => { context.clearRect(0, 0, canvasWidth, canvasHeight) context.fillStyle = '#00f4cc' context.fillRect(playerX, playerY, playerWidth, playerHeight) context.fillStyle = 'red' context.fillRect(enemy1X, enemy1Y, enemy1Width, enemy1Height) requestAnimationFrame(drawFun); } const animationId = drawFun(); return () => cancelAnimationFrame(animationId); }, [playerX, playerY, playerWidth, playerHeight, enemy1X, enemy1Y, enemy1Width, enemy1Height]); return <canvas ref={canvasRef} width={800} height={600} />; }
3. 直接使用最新变量避免闭包问题
若不想每次变量变化都重启动画,可在drawFun中直接引用外部变量,每次执行时获取最新值:
const drawFun = () => { context.clearRect(0, 0, canvasWidth, canvasHeight) context.fillStyle = '#00f4cc' context.fillRect(playerX, playerY, playerWidth, playerHeight) context.fillStyle = 'red' context.fillRect(enemy1X, enemy1Y, enemy1Width, enemy1Height) requestAnimationFrame(drawFun); }
内容的提问来源于stack exchange,提问作者user20115099
相关产品推荐
相关产品推荐

