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

React中useRef绑定Canvas报错:Cannot read properties of undefined (reading 'getContext')

解决useRef绑定canvas时Cannot read properties of undefined (reading 'getContext')错误

问题根源

  • 组件首次渲染阶段,canvas元素还未挂载到DOM树,此时canvasRef.current的值为undefined,直接调用getContext必然抛出错误。
  • 错误地将width和height属性添加到了useRef返回的ref对象本身,而非真实的canvas DOM元素(即canvasRef.current)上。

解决方案

使用React的useEffect钩子,确保在DOM挂载完成后再操作canvas元素。useEffect的回调函数会在组件挂载完成后执行,此时canvasRef.current已指向真实的canvas节点。


修正后的完整代码

import { useRef, useEffect } from 'react';

function Landing() {
  const canvasRef = useRef();
  const contextRef = useRef();
  const frameCount = 230;
  const images = [];
  let ball = { frame: 0 };

  // 预加载所有帧图片
  const currentFrame = (index) =>
    `../../assets/landing-animations/${(index + 1).toString()}.jpg`;
  
  for (let i = 0; i < frameCount; i++) {
    const img = new Image();
    img.src = currentFrame(i);
    images.push(img);
  }

  useEffect(() => {
    // 确保canvas元素已挂载
    if (!canvasRef.current) return;

    const canvas = canvasRef.current;
    const context = canvas.getContext("2d");
    contextRef.current = context;

    // 设置并监听canvas尺寸
    const setCanvasSize = () => {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
    };

    setCanvasSize();
    window.addEventListener('resize', setCanvasSize);

    // 组件卸载时清理监听
    return () => {
      window.removeEventListener('resize', setCanvasSize);
    };
  }, []);

  function render() {
    // 提前判断避免空值报错
    if (!contextRef.current || !canvasRef.current) return;
    
    const canvas = canvasRef.current;
    contextRef.current.clearRect(0, 0, canvas.width, canvas.height);
    contextRef.current.drawImage(images[ball.frame], 0, 0, canvas.width, canvas.height);
  }

  // 在此处添加GSAP滚动动画逻辑,示例:
  // gsap.to(ball, {
  //   frame: frameCount - 1,
  //   scrub: 1,
  //   onUpdate: render
  // })

  return (
    <div className="landing">
      <h1 className="landing__header">Welcome to my portfolio</h1>
      <canvas className="landing__canvas" ref={canvasRef}></canvas>
    </div>
  );
}

关键修正说明

  1. useEffect的使用:空依赖数组[]确保代码只在组件挂载后执行一次,此时DOM已渲染完成,canvasRef.current有效。
  2. canvas尺寸设置:将width/height直接绑定到canvasRef.current(真实DOM元素),而非ref对象本身。
  3. 上下文缓存:用contextRef存储canvas上下文,避免重复调用getContext。
  4. 空值检查:在render函数中增加存在性判断,防止意外报错。
  5. 窗口resize监听:保证canvas尺寸随窗口变化自动更新(可选但实用)。

内容的提问来源于stack exchange,提问作者Omar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 17:40:44