首页 > 解决方案 > 画布绘图:等待图像以递归方法加载

问题描述

我正在实现一个递归方法,该方法采用一组图像并将它们绘制到画布中:

async drawImages(ctx, images): Promise<any> {
    const img = images.shift();
    const imgToDraw = new Image();
    imgToDraw.src = img.src;
    imgToDraw.onload = () => {
        ctx.drawImage(imgToDraw, img.x, img.y, img.w, img.h);
        if (images.length > 0) {
            this.drawImages(ctx, images);
        } else {
            console.log("I - Processing");
            return Promise.resolve();
        }
    }
}

在这个方法的执行结束时,我需要检索画布的 DataURL,我这样做是这样的:

someCallingMethod() {
    //...
    await this.drawImages(ctx, images);
    console.log("II - Finished");
    console.log(canvas.toDataURL());
}

我面临的问题是 await 不适用于该drawImages方法。执行此代码时,我总是在"II - Finished"日志之前获取日志"I - Processing",因此canvas.toDataURL()从不包含应该绘制到画布上的图像。

谢谢

标签: javascripttypescripthtml5-canvas

解决方案


您需要同步返回 Promise。
在这里,您将在异步onload事件处理程序中返回它。
所以drawImages不会返回 Promise 但未定义。

此外,您将希望返回drawImages每次的下一次迭代,以便您的外部await等待所有这些 Promise:

const obj = {
  async drawImages(ctx, images) {
    // return a Promise synchronously
    return new Promise((resolve, reject) => {
      const img = images.shift();
      const imgToDraw = new Image();
      imgToDraw.src = img.src;
      imgToDraw.onload = () => {
        ctx.drawImage(imgToDraw, img.x, img.y, img.w, img.h);
        if (images.length > 0) {
          // resolve with next iteration so we can await all
          resolve(this.drawImages(ctx, images));
        } else {
          console.log("I - Processing");
          // done
          resolve();
        }
      };
      imgToDraw.onerror = reject;
    });
  }
};

(async () => {
  const images = [];
  // picsum.photos doesn't fill all the indexes...
  const urls = ["01", "02", "03", "04", "05", "06", "10", "11", "12", "13"];
  const s = 50; // image size
  for(let i=0; i<10; i++) {
    images.push( {
      src: `https://picsum.photos/${s}/${s}?image=10` + urls[i],
      x: (i%5)*s,
      y: Math.floor(i/5)*s,
      w: s,
      h: s
    });
  }
  const ctx = canvas.getContext('2d');
  await obj.drawImages(ctx, images);
  // fill a green rect over to show we are able to await it
  ctx.fillStyle = 'rgba(0,255,0, 0.2)';
  ctx.fillRect(0,0,canvas.width,canvas.height);
  console.log("all done");
})();
<canvas id="canvas" width="250" height="100"></canvas>


推荐阅读