等待在 JavaScript 中加载图像

我正在进行一个 Ajax 调用,它会返回一些包括图像路径在内的信息。

我准备所有这些信息在我的 HTML 将显示为一种弹出窗口。我只是将 by popup div 的可见性从隐藏切换到可见。

要设置弹出式 div 的位置,我必须根据图像的高度进行计算。因此,我必须等待图像加载知道其尺寸之前,设置位置和切换可见性到可见。

我尝试了递归、 setTimeout、完整的 img 属性、 while loop 等技巧,但都没有成功。

那么,我该怎么做呢? 也许我应该在 Ajax 调用中返回维度。

126274 次浏览

If you use jQuery, you can use its load event.

Have a look at the example:

$('img.userIcon').load(function(){
if($(this).height() > 100) {
$(this).addClass('bigImg');
}
});
var img = new Image();
img.onload = function() { alert("Height: " + this.height); }
img.src = "http://path/to/image.jpg";

Note that it's important to do it in the order above: First attach the handler, then set the src. If you do it the other way around, and the image is in cache, you may miss the event. JavaScript is run on a single thread in browsers (unless you're using web workers), but browsers are not single-threaded. It's perfectly valid for the browser to see the src, identify the resource is available, load it, trigger the event, look at the element to see if it has any handlers that need to be queued for callback, not see any, and complete the event processing, all between the src line and the line attaching the handler. (The callbacks wouldn't happen between the lines if they were registered, they'd wait in the queue, but if there aren't any, the event isn't required to wait.)

I had a slow CAPTCHA (1st_image) image loading on my page and I wanted to display another image (2nd_image) only AFTER the CAPTCHA image (1st_image) is loaded. So I had to wait for the CAPTCHA (1st_image) image to load first.

Here is the solution that works perfectly fine to wait for an image to load first and then load another image (don't forget to display a "please wait!" image while they are waiting for other image to load):

<script>
function checkImageLoad() {
if (document.getElementById("1st_image").complete == true) {
console.log("1st_image Loaded!");
}
document.getElementById("2nd_image").src = "http://example.org/2nd_image.png";
}
</script>
<body onload="checkImageLoad();">
<img id="1st_image" src="http://example.org/1st_image.png">
<img id="2nd_image" src="http://example.org/loading_please_wait.gif">

Either the Internet speed is slow or fast, and the browser will wait for the whole page to load with all the images (even if they are linked externally) and then execute the function, so the second image is displayed only after the first image loads fine.

Advanced note: You can use a web page URL in the "src" of the image to load a web page first and then show the image. The purpose is to load the cookie from the external webpage which might effect the second image displayed (like CAPTCHA).

just wrap your image onload in a function with a promise and then call it with await.

async drawImg(ctx, image){


return new Promise(resolve => {


image.onload = function () {
ctx.drawImage(image, 10, 10, 200, 180);
resolve('resolved');
}


});


}

it should work just fine

The accepted answer is outdated but does show the basic Image#onload callback approach. Nowadays, you'll likely want to promisify the image load to avoid callback hell.

This answer is a good shot at promisifying the image onload handler, but is missing some key points as my comment indicates.

Here's another promisification of Image that is a bit more general. Rejecting in the onerror handler and passing the actual image object into the resolver are important to make the function minimally reusable.

Improvements might include additional parameters (such as crossOrigin, for example). A settings object or providing an Image as a parameter is another approach to generalize the function (setting src fires the request, so that should go last after handlers have been added).

const loadImage = src =>
new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
})
;


loadImage("http://placekitten.com/90/100").then(image =>
console.log(image, `\nloaded? ${image.complete}`)
);

With the above function, Promise.all can be used to load a batch of images in parallel (Promise.allSettled is useful if you want to keep going even if some images don't load).

const loadImage = src =>
new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
})
;


const imageUrls = [
"http://placekitten.com/85/150",
"http://placekitten.com/85/130",
"http://placekitten.com/85/110",
];
Promise.all(imageUrls.map(loadImage)).then(images => {
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");
images.forEach((image, i) =>
ctx.drawImage(image, i * 90, 0, image.width, image.height)
);
});

How about the window load event?

window.addEventListener('load', (event) => {
//do stuff with images
});

or

window.onload = (event) => {
//do stuff with images
};

https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event

This worked for me where I needed the browser to calculate the size of the images before running a layout script.

Function to wait for image, that is already in HTML.

Promise example. Does not rejects just waits for load or fail:

function waitForImage(imgElem) {
return new Promise(res => {
if (imgElem.complete) {
return res();
}
imgElem.onload = () => res();
imgElem.onerror = () => res();
});
}






// test
(async () => {
const img = document.querySelector('img');
// to not to cache in the test, set src dynamically
img.src = 'https://www.gravatar.com/avatar/71094909be9f40bd576b1974a74966aa?s=48&d=identicon&r=PG&f=1';


console.log('height before', img.naturalHeight); // 0
await waitForImage(img);
console.log('height after', img.naturalHeight); // 48
})();
<img src=""/>

Version with reject, if you need to know, if its failed:

function waitForImage(imgElem) {
return new Promise((res, rej) => {
if (imgElem.complete) {
return res();
}
imgElem.onload = () => res();
imgElem.onerror = () => rej(imgElem);
});
}






// test
(async () => {
const img = document.querySelector('#rightone');
// to not to cache in the test, set src dynamically
img.src = 'https://www.gravatar.com/avatar/71094909be9f40bd576b1974a74966aa?s=48&d=identicon&r=PG&f=1';


console.log('height before', img.naturalHeight); // 0
await waitForImage(img); // success
console.log('height after', img.naturalHeight); // 48


try {
const failImg = document.querySelector('#wrongone');
failImg.src = 'https://wrongimage';
await waitForImage(failImg); // rejects after some time
} catch(e) {
console.log('failed to load image', e)
}




})();
<img id="rightone" src="">
<img id="wrongone" src="">