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

Next.js中ReferenceError: Image is not defined问题解决求助

解决Next.js中ReferenceError: Image is not defined的问题

核心原因是你虽添加了"use client"指令,但组件顶层代码(包括new Image())仍会在服务端渲染阶段执行,而Image是浏览器专属API,服务端环境不存在这个构造函数。以下是具体修复方案:

方案1:将Image相关逻辑移到useEffect中

useEffect的回调函数仅在浏览器端的组件挂载/更新阶段执行,完全避开服务端渲染。同时修复你代码中直接在组件顶层调用setinit导致的无限重渲染问题:

"use client";
import { useState, useRef, useEffect } from "react";
import User from "@/app/services/operations/user";
import { useSelector } from "react-redux";
import ShineButton from "../button/button";

const Canvas = ({ tag, init, setinit }) => {
  const { email } = useSelector((state) => state.User);
  const [imagename, setimagename] = useState("");
  const { uploadtocloud } = User();
  const ref = useRef();

  // 处理图片加载和Canvas绘制逻辑,仅在浏览器执行
  useEffect(() => {
    // 仅当图片URL存在时执行
    if (!tag.imageurl) return;

    // 更新init状态,避免顶层触发导致无限重渲染
    setinit(1);

    const pixelRatio = window.devicePixelRatio || 1;
    const image = new Image();
    image.src = tag.imageurl;
    image.setAttribute("crossorigin", "anonymous");

    const handleImageLoad = () => {
      const canvas = ref.current;
      if (!canvas) return;

      const context = canvas.getContext("2d");
      canvas.width = 900 * pixelRatio;
      canvas.height = 600 * pixelRatio;
      canvas.style.width = "900px";
      canvas.style.height = "600px";
      context.scale(pixelRatio, pixelRatio);
      context.font = "30px Arial";
      context.textAlign = "center";
      context.drawImage(image, 0, 0, 900, 600);

      const data = tag.quotedata[0]?.quote;
      if (!data) return;

      const arr = data.split(" ");
      const farr = [];
      let str = "";
      for (let i = 0; i < arr.length; i++) {
        if (str.length < 30) {
          str += arr[i] + " ";
        } else {
          farr.push(str.trim());
          str = arr[i] + " ";
        }
      }
      farr.push(str.trim());

      let x = canvas.width / 2 / pixelRatio - 200; // 适配pixelRatio后的坐标
      let y = canvas.height / 2 / pixelRatio - 220;
      for (let i = 0; i < farr.length; i++) {
        context.fillText(farr[i], x, y);
        y += 50;
      }
    };

    image.addEventListener("load", handleImageLoad);
    // 清理函数,避免内存泄漏
    return () => {
      image.removeEventListener("load", handleImageLoad);
    };
  }, [tag.imageurl, tag.quotedata, setinit]); // 依赖项变化时重新执行

  async function handleonsubmit(e) {
    e.preventDefault();
    const canvas = ref.current;
    if (!canvas) return;

    let imageBlob = await new Promise((resolve) =>
      canvas.toBlob(resolve, "image/png")
    );
    uploadtocloud(imageBlob, imagename, email);
  }

  const handleDownload = () => {
    const canvas = ref.current;
    if (!canvas) return;

    const dataURL = canvas.toDataURL("image/jpeg");
    const link = document.createElement("a");
    link.href = dataURL;
    link.download = `${Date.now()}.jpg`;
    link.click();
  };

  return (
    <div>
      <canvas ref={ref} className={init === 0 ? "dnone" : "normal"} />
      <div style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
        <div onClick={handleDownload}>
          <ShineButton value="Download" />
        </div>
      </div>
      <form onSubmit={handleonsubmit} style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
        <div style={{ color: "white" }}>Upload To My Creations</div>
        <input
          type="text"
          onChange={(e) => setimagename(e.target.value)}
          value={imagename}
          placeholder="Enter image name"
        />
        <ShineButton value="Upload" type="submit" />
      </form>
    </div>
  );
};

export default Canvas;

关键修改点说明

  • 将new Image()及后续的图片加载、Canvas绘制逻辑全部移到useEffect回调内,确保仅在浏览器端执行。
  • 把顶层的setinit(1)移到useEffect中,避免每次组件渲染都触发状态更新导致无限重渲染。
  • 修复了Canvas坐标计算的小问题:因为设置了context.scale(pixelRatio, pixelRatio),所以文字坐标需要除以pixelRatio才能正确居中。
  • 添加了必要的空值判断(比如canvas、data),避免潜在的空指针错误。
  • 添加了事件监听器的清理函数,防止内存泄漏。

替代方案:条件判断浏览器环境

如果不想用useEffect,也可以通过判断typeof window !== 'undefined'来确保只在浏览器端创建Image:

let image;
if (typeof window !== 'undefined') {
  image = new Image();
  // 后续的src设置、事件监听等逻辑也要放在这个判断里
}

但这种方式不如useEffect优雅,因为还是会有顶层代码分支,推荐优先使用useEffect方案。

内容的提问来源于stack exchange,提问作者bhargava ram a

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 14:51:04