1

我尝试使用 fetch 和 async/await 加载图像并将其显示在画布上,但它不起作用。图像未在画布上绘制。它正确加载到 HTML 中的 img 元素中。在画布上,如果我将绘图代码包装在 setTimeout 中,它可以工作,但我不想这样做。

有没有办法使用等待加载图像并在没有 setTimeout 的情况下在画布上绘制和绘制?

这是代码:

async function loadImage(url) {
    let response = await fetch(url);
    let blob = await response.blob();
    return URL.createObjectURL(blob);
}

let canvas = document.body.querySelector("canvas");
let ctx = canvas.getContext("2d");

let tileURL = loadImage("https://avatars.githubusercontent.com/u/92330684?s=120&v=4").then((tileURL) => {
        
    // Displaying image element in HTML works.
    let img = document.body.querySelector("img");
    img.src = tileURL;
    
    // Displaying the image immediately in canvas doesn't work.
    ctx.drawImage(img, 0, 0);
    
    // But it works if I add some delay.
    setTimeout(() => {
         ctx.drawImage(img, 100, 0);
    }, 3000); // 3 second delay.
    
});
canvas {
   border: 1px solid #000;
}
<canvas></canvas>
<img>

4

2 回答 2

2

加载图像始终是异步的,无论来源*。您需要等待它已加载,然后才能对其进行任何操作。

现在,不清楚你为什么在fetch这里使用,你可以.src直接将图像设置为 URL:

const canvas = document.body.querySelector("canvas");
const ctx = canvas.getContext("2d");

// Displaying image element in HTML works.
const img = document.body.querySelector("img");
img.crossOrigin = "anonymous"; // allow read-back
img.src = "https://avatars.githubusercontent.com/u/92330684?s=120&v=4";
img.onload = (evt) => // or await img.decode() if in async
  ctx.drawImage(img, 0, 0);
canvas {
   border: 1px solid #000;
}
<canvas></canvas>
<img>

但是,如果您有一个 Blob(或实际上是被迫使用),则直接从该 Blobfetch创建一个ImageBitmap 。这是生成和存储 CanvasImageSource(将在 中使用drawImage())的最高效方式。
对于不支持此方法的旧浏览器,我通过我的这个 polyfill 为您提供了帮助。

async function loadImage(url) {
  const response = await fetch(url);
  const blob = response.ok && await response.blob();
  return createImageBitmap(blob);
}

const canvas = document.body.querySelector("canvas");
const ctx = canvas.getContext("2d");
loadImage("https://avatars.githubusercontent.com/u/92330684?s=120&v=4").then((bitmap) => {
  ctx.drawImage(bitmap, 0, 0);
});
canvas {
   border: 1px solid #000;
}
<!-- createImageBitmap polyfill for old browsers --> <script src="https://cdn.jsdelivr.net/gh/Kaiido/createImageBitmap/dist/createImageBitmap.js"></script>
<canvas></canvas>
<!-- HTMLImage is not needed -->

*缓存的图像可能在下一次调用之前就准备好了drawImage(),但永远不要假设是这样。

于 2021-12-09T01:16:46.903 回答
1

即使您使用的是内存中的 blob URL,它仍然需要由浏览器加载。

img在将其添加到画布之前,您无需等待加载,因此它添加的所有内容都是空白。

注册一个加载事件处理程序或使用较新的HTMLImageElement.decode()等待图像准备好,然后再将其添加到画布。

async function loadImage(url) {
  let response = await fetch(url);
  let blob = await response.blob();
  return URL.createObjectURL(blob);
}

const img = document.querySelector("img");
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

const url = "https://avatars.githubusercontent.com/u/92330684?s=120&v=4"

loadImage(url).then(async (tileURL) => {

  // Displaying image element in HTML works.
  img.src = tileURL;
  
  // wait for it to load
  await img.decode()
  
  ctx.drawImage(img, 0, 0);
});
canvas {
  border: 1px solid #000;
}
<canvas></canvas>
<img>

于 2021-12-08T22:43:17.717 回答