使用 fetch 和 async/await 加载图像后在 canvas 上绘制图像时出现问题
Problem with drawing image on canvas after loading it with fetch and async/await
我尝试使用 fetch 和 async/await 加载图像并在 canvas 上显示它,但它不起作用。图片未在 canvas 上绘制。它正确加载到 HTML 中的 img 元素中。在 canvas 上,如果我将绘图代码包装在 setTimeout 中它可以工作,但我宁愿不这样做。
有没有什么方法可以在 canvas 上使用 await 和 fetch 加载图像并在没有 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>
即使您使用的是内存中的 blob URL,它仍然需要由浏览器加载。
您在将 img
添加到 canvas 之前没有等待加载,所以它添加的只是一个空白。
注册一个 load 事件处理程序或使用较新的 HTMLImageElement.decode() 等待图像准备就绪,然后再将其添加到 canvas。
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>
加载图像始终是异步的,无论来源如何*。您需要等待它加载完毕才能对其进行任何操作。
现在,不清楚你为什么在这里使用 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(或者实际上被迫使用 fetch
),那么直接从这个 Blob 创建一个 ImageBitmap。这是生成和存储 CanvasImageSource 的最高效方法(将在 drawImage()
中使用)。
对于不支持此方法的旧浏览器,我通过 this polyfill of mine.
为您介绍
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()
之前准备就绪,但永远不要这样假设。
我尝试使用 fetch 和 async/await 加载图像并在 canvas 上显示它,但它不起作用。图片未在 canvas 上绘制。它正确加载到 HTML 中的 img 元素中。在 canvas 上,如果我将绘图代码包装在 setTimeout 中它可以工作,但我宁愿不这样做。
有没有什么方法可以在 canvas 上使用 await 和 fetch 加载图像并在没有 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>
即使您使用的是内存中的 blob URL,它仍然需要由浏览器加载。
您在将 img
添加到 canvas 之前没有等待加载,所以它添加的只是一个空白。
注册一个 load 事件处理程序或使用较新的 HTMLImageElement.decode() 等待图像准备就绪,然后再将其添加到 canvas。
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>
加载图像始终是异步的,无论来源如何*。您需要等待它加载完毕才能对其进行任何操作。
现在,不清楚你为什么在这里使用 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(或者实际上被迫使用 fetch
),那么直接从这个 Blob 创建一个 ImageBitmap。这是生成和存储 CanvasImageSource 的最高效方法(将在 drawImage()
中使用)。
对于不支持此方法的旧浏览器,我通过 this polyfill of mine.
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()
之前准备就绪,但永远不要这样假设。