You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 23:35:21