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

Canvas在三星Mobile Chrome中无法绘制全部图片的问题排查

Canvas绘制异常问题(三星Mobile Chrome专属)

问题描述

仅在三星设备的Mobile Chrome浏览器中,Canvas无法绘制全部图片(仅部分图片能显示),其他设备无此问题。现象表现为部分图片可正常绘制,部分完全无法显示;偶尔还会出现已绘制的部分图片被替换的情况(因速度过快难以确认)。

相关代码

const drawSymbol = (
  ctx: CanvasRenderingContext2D,
  symbol: CardSymbol,
  image: HTMLImageElement,
) => {
  window.requestAnimationFrame(() => {
    ctx.drawImage(
      image,
      symbol.x,
      symbol.y,
      symbol.width,
      symbol.height,
    );
  });
};

useEffect(() => {
  const canvas = canvasRef.current;
  if (canvas) {
    const ctx = canvas.getContext('2d');
    if (ctx) {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      for (const symbol of card) {
        const image = images[symbol.image.id];
        if (image.loadedImg) {
          drawSymbol(ctx, symbol, image.loadedImg);
        } else {
          const img = new Image();
          img.onload = () => {
            image.loadedImg = img; // Cache loaded image
            drawSymbol(ctx, symbol, img);
          };
          img.src = image.src;
        }
      }
    }
  }
}, [card]);

可能原因分析

  • 分散的requestAnimationFrame回调冲突:当前每个图片绘制都单独触发requestAnimationFrame,三星Mobile Chrome的渲染线程可能对高频异步回调存在调度限制,导致部分回调被丢弃或延迟,进而出现绘制不完整。
  • Canvas上下文竞争:多个异步绘制回调共享同一个上下文,若card更新触发新的useEffect,新的clearRect会覆盖还未执行的旧绘制操作,或者回调执行顺序混乱导致图片被替换。
  • 三星Chrome的定制化渲染策略:三星定制的Chrome可能在图片加载优先级、Canvas资源分配上有特殊处理,分散的异步绘制请求无法被正确调度。

统一使用单个requestAnimationFrame的效果

使用单个requestAnimationFrame统一绘制所有图片大概率能解决问题,原因如下:

  1. 所有绘制操作集中在同一帧完成,避免了大量分散回调抢占渲染资源,保证绘制的原子性和顺序性。
  2. 确保clearRect与所有绘制操作在同一帧执行,不会被后续useEffect的清理操作打断,消除上下文竞争问题。

优化代码示例

useEffect(() => {
  const canvas = canvasRef.current;
  if (!canvas) return;
  
  const ctx = canvas.getContext('2d');
  if (!ctx) return;

  // 收集所有绘制任务(已加载图片直接加入,未加载的转为Promise等待)
  const drawTasks: Promise<{symbol: CardSymbol, image: HTMLImageElement}>[] = [];
  
  for (const symbol of card) {
    const imageData = images[symbol.image.id];
    if (imageData.loadedImg) {
      drawTasks.push(Promise.resolve({symbol, image: imageData.loadedImg}));
    } else {
      drawTasks.push(new Promise(resolve => {
        const img = new Image();
        img.onload = () => {
          imageData.loadedImg = img;
          resolve({symbol, image: img});
        };
        img.src = imageData.src;
      }));
    }
  }

  // 等待所有图片就绪后,统一在一帧内完成绘制
  Promise.all(drawTasks).then(tasks => {
    window.requestAnimationFrame(() => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      tasks.forEach(({symbol, image}) => {
        ctx.drawImage(image, symbol.x, symbol.y, symbol.width, symbol.height);
      });
    });
  });
}, [card]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 05:22:39