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

React 18中HTML Canvas图片渲染不稳定问题求助

问题原因与修复方案

核心问题

第二张图片(logo)无法渲染或消失的根本原因是图片加载的异步性:设置logoImage.src后立刻调用context.drawImage,此时图片大概率还没加载完成,Canvas无法绘制未加载完成的图片。此外还有几个次要问题会加剧这个现象:

  • 错误处理函数绑定在src设置之后,无法捕获加载初期的错误
  • Canvas尺寸依赖未加载完成的image.width,导致尺寸异常间接影响绘制

修复步骤

  1. 等待图片加载完成后再绘制:用onload事件或Promise封装图片加载逻辑,确保两张图片都加载完成后再执行Canvas绘制
  2. 提前绑定错误/加载事件:在设置src之前绑定onload和onerror,保证能正确捕获加载状态
  3. 避免依赖未加载图片的尺寸:要么用固定尺寸初始化Canvas,要么在图片加载完成后再动态调整

修改后的代码示例

// 封装图片加载的Promise工具函数
const loadImage = (src) => {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = (err) => reject(err);
    img.src = src;
  });
};

const handleDraw = async (
  msg,
  fontSize,
  selectedFont,
  textColor,
  boldStyle,
  italicStyle,
  underlineStyle
) => {
  const canvas = canvasRef.current;
  if (!canvas) return;
  const context = canvas.getContext("2d");
  const imgElement = document.getElementById("cardImage");
  if (!imgElement) return;

  try {
    // 等待两张图片加载完成
    const [image, logoImage] = await Promise.all([
      loadImage(imageContent),
      loadImage(logo)
    ]);

    // 加载完成后再设置Canvas尺寸
    canvas.width = image.width + 1200;
    canvas.height = image.height + 500;

    // 绘制主图
    context.drawImage(image, 0, 0, imgElement.width + 150, imgElement.height - 20);
    // 绘制Logo(此时图片已加载完成)
    context.drawImage(logoImage, imgElement.width + 200, 0, 250, 100);

    // 设置文本样式并绘制
    context.font = `${boldStyle ? "bold" : "normal"} ${italicStyle ? "italic" : "normal"} ${fontSize}px ${selectedFont}`;
    context.fillStyle = textColor;
    drawText(context, 400, 40, msg, imgElement.width + 160, imgElement.height / 6);
  } catch (err) {
    console.error("图片加载或绘制出错:", err);
  }
};

function drawText(context, maxWidth, lineHeight, text, x, y) {
  const lines = text.split("\n");
  const newLines = [];
  for (const line of lines) {
    let words = line.split(" ");
    let currentLine = words[0];
    for (let i = 1; i < words.length; i++) {
      const width = context.measureText(`${currentLine} ${words[i]}`).width;
      if (width > maxWidth) {
        newLines.push(currentLine);
        currentLine = words[i];
      } else {
        currentLine += ` ${words[i]}`;
      }
    }
    newLines.push(currentLine);
  }

  for (const line of newLines) {
    context.fillText(line, x, y);
    y += lineHeight;
  }
}

关键说明

  • 用Promise.all同时加载两张图片,确保两者都准备好再绘制
  • 把Canvas尺寸调整逻辑放在图片加载完成后,避免依赖未初始化的图片尺寸
  • 修复了context.font的空格问题(原代码中多余的空格可能导致字体样式失效)
  • 增加了Canvas和DOM元素的存在性检查,避免空指针错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 05:55:22