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

React如何在切换至gameboard前完成图片加载与数据转换?

问题原因及解决方案

问题根源

你的代码核心问题在于图片加载是异步操作:convertAllLayouts函数里的img.onload回调要等图片加载完成后才会执行,但函数本身会立刻返回空的convertedLayouts数组。之后useEffect直接用这个空数组更新layoutShapes,并马上切换到gameboard页面,等图片实际加载完成并转换数据时,页面已经渲染完毕,自然无法拿到正确的布局数据。

解决方案

把图片加载和转换逻辑改成异步Promise形式,等待所有图片处理完成后再更新状态、切换页面。具体修改如下:

1. 重构convertAllLayouts为异步函数

将每个图片的加载和转换封装成Promise,用Promise.all等待所有任务完成后返回结果:

async function convertAllLayouts(ref, presets) {
  if (presets.length === 0) return [];

  // 把每个图片的处理逻辑封装成Promise
  const conversionPromises = presets.map(presetUrl => {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.crossOrigin = 'Anonymous';
      
      const ctx = ref.current.getContext('2d', { 
        alpha: true, 
        desynchronized: false, 
        colorSpace: 'srgb', 
        willReadFrequently: true
      });
      ctx.globalCompositeOperation = "copy";

      img.onload = function () {
        ctx.imageSmoothingEnabled = false;
        ctx.drawImage(img, 0, 0);
        
        const imageData = ctx.getImageData(0, 0, 16, 16, {colorSpace: 'srgb'}).data;
        const newConvertedPreset = [];

        for (let rowIndex = 0; rowIndex < 16; rowIndex++) {
          const newRow = [];
          // 计算当前行的像素起始位置
          const rowStart = rowIndex * 16 * 4;
          for (let i = rowStart; i < rowStart + 16 * 4; i += 4) {
            // 根据alpha通道判断像素值
            newRow.push(imageData[i + 3] === 255 ? 1 : 0);
          }
          newConvertedPreset.push(newRow);
        }
        resolve(newConvertedPreset);
      };

      // 处理图片加载失败的情况
      img.onerror = function () {
        reject(new Error(`Failed to load image: ${presetUrl}`));
      };

      img.src = presetUrl;
    });
  });

  // 等待所有图片转换完成
  return Promise.all(conversionPromises);
}

2. 修改useEffect使用async/await等待结果

在useEffect里定义异步处理函数,等待convertAllLayouts完成后再更新状态并切换页面:

useEffect(() => {
  // 定义异步处理函数
  const handleLayoutConversion = async () => {
    if (!isCustomLayoutShapesSubmitClicked) return;

    try {
      const layouts = await convertAllLayouts(canvasRef, presetLayouts);

      if (layouts.length === 0) {
        // 生成默认布局(注意:需确保tileDimensions变量已提前定义)
        let defaultLayoutShape = [];
        for (let rowIndex = 0; rowIndex < tileDimensions.h; rowIndex++) {
          const newRow = new Array(tileDimensions.w).fill(1);
          if ((tileDimensions.h * tileDimensions.w) % 2 !== 0) {
            if (rowIndex === Math.floor(tileDimensions.h / 2)) {
              newRow[Math.floor(tileDimensions.w / 2)] = 0;
            }
          } 
          defaultLayoutShape.push(newRow);
        }
        setLayoutShapes([defaultLayoutShape, 'default']);
      } else {
        setLayoutShapes([...layouts]);
      }

      // 所有处理完成后再切换页面
      setDisplay('gameboard');
    } catch (error) {
      console.error('Layout conversion failed:', error);
      // 可在此添加错误提示或回退逻辑
    } finally {
      setIsCustomLayoutShapesSubmitClicked(false);
    }
  };

  handleLayoutConversion();
}, [isCustomLayoutShapesSubmitClicked])

3. 额外注意点

  • 确保tileDimensions变量在组件内已定义,原代码中未看到该变量的声明,需补充。
  • 建议添加加载状态(比如按钮置灰、显示加载动画),避免用户重复点击触发多次转换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 06:34:54