admin管理员组文章数量:1353527
I'm fairly new to Javascript and I'm currently trying to display images at various sizes and positions. This means I have to load the images first, before I can access values like width and height. Now, here's where I'm facing problems.
I tried loading the images one after another, making sure another image would only be loaded when one image is pleted.
const a = new Image();
const b = new Image();
const images = [
a,
b
];
const imageLinks = [
'a.png',
'b.png'
];
let loaded = 0;
for (let i = 0; i < images.length; i++) {
images[i].onload = function() {
console.log(images[i].width);
console.log(images[i].height);
loaded++;
}
images[i].src = imageLinks[i];
}
console.log(images[0].width);
console.log(images[0].height);
Obviously, this doesn't produce the correct result. The Logs after the for-loop are still 0 and are printed before the Logs inside the for-loop. That means, the program doesn't wait for the images to finish.
Next, I tried this:
let loaded = 0;
while (loaded < images.length) {
images[loaded].onload = function() {
console.log(images[loaded].width);
console.log(images[loaded].height);
loaded++;
}
images[loaded].src = imageLinks[loaded];
}
console.log(images[0].width);
console.log(images[0].height);
This does even worse. The page doesn't even load in the first place, it seems to be stuck forever in the while-loop. This makes sense because I reckon that everytime images[loaded].src
is set, the loading process starts over, thus never making any progress.
Any solutions I've found include HTML-code, where images are loaded from HTML via document.querySelector(), which I cannot use, or are way too plicated for me to even try to wrap my head around as someone who's just starting out. I don't need a perfect solution, for now I just need something that works. It can't be that plicated, right? I just want to load a few images. I'm really stuck here.
I'm fairly new to Javascript and I'm currently trying to display images at various sizes and positions. This means I have to load the images first, before I can access values like width and height. Now, here's where I'm facing problems.
I tried loading the images one after another, making sure another image would only be loaded when one image is pleted.
const a = new Image();
const b = new Image();
const images = [
a,
b
];
const imageLinks = [
'a.png',
'b.png'
];
let loaded = 0;
for (let i = 0; i < images.length; i++) {
images[i].onload = function() {
console.log(images[i].width);
console.log(images[i].height);
loaded++;
}
images[i].src = imageLinks[i];
}
console.log(images[0].width);
console.log(images[0].height);
Obviously, this doesn't produce the correct result. The Logs after the for-loop are still 0 and are printed before the Logs inside the for-loop. That means, the program doesn't wait for the images to finish.
Next, I tried this:
let loaded = 0;
while (loaded < images.length) {
images[loaded].onload = function() {
console.log(images[loaded].width);
console.log(images[loaded].height);
loaded++;
}
images[loaded].src = imageLinks[loaded];
}
console.log(images[0].width);
console.log(images[0].height);
This does even worse. The page doesn't even load in the first place, it seems to be stuck forever in the while-loop. This makes sense because I reckon that everytime images[loaded].src
is set, the loading process starts over, thus never making any progress.
Any solutions I've found include HTML-code, where images are loaded from HTML via document.querySelector(), which I cannot use, or are way too plicated for me to even try to wrap my head around as someone who's just starting out. I don't need a perfect solution, for now I just need something that works. It can't be that plicated, right? I just want to load a few images. I'm really stuck here.
Share Improve this question edited Feb 25, 2023 at 5:56 RobsonDaSheep asked Feb 25, 2023 at 5:53 RobsonDaSheepRobsonDaSheep 111 silver badge4 bronze badges 5-
Access the width and height in the
onload
callbacks only. Move the code that needs those values into the event handler. – Unmitigated Commented Feb 25, 2023 at 5:57 - 1 Does this answer your question? How to return values from async functions using async-await from function? – Andy Ray Commented Feb 25, 2023 at 5:59
- @Unmitigated The problem is that the program just keeps running and using 0 as its values for width and height. I need those values all over my code and I can't move everything into the event handler. – RobsonDaSheep Commented Feb 25, 2023 at 6:02
-
Unfortunately, that's what you need to do. Put everything in one big function. You can use promises and
Promise.all()
to wait for all the images to be loaded, then call that function. – Barmar Commented Feb 25, 2023 at 6:05 - 1 Async behavior in JS results in dozens of these exact types of questions asked on SO every day. Here's a recent exact duplicate of this one stackoverflow./questions/75563660/… – Andy Ray Commented Feb 25, 2023 at 6:26
4 Answers
Reset to default 9Here is another promise-based answer:
const images = [...document.querySelectorAll("div img")];
const proms=images.map(im=>new Promise(res=>
im.onload=()=>res([im.width,im.height])
))
// list all image widths and heights _after_ the images have loaded:
Promise.all(proms).then(data=>{
console.log("The images have loaded at last!\nHere are their dimensions (width,height):");
console.log(data);
})
// Everything is set up here.
// Except: the images don't have their `src` attributes yet!
// These are added now and the action will unfold:
images.forEach((im,i)=>im.src=`https://picsum.photos/id/${i+234}/200/100`);
<div><img><img></div>
A minimalist form of the above could look like this:
const images = [...document.querySelectorAll("div img")];
// list all image widths and heights _after_ the images have loaded:
Promise.all(images.map(im=>new Promise(resolve=>im.onload=resolve))).then(()=>{
console.log("The images have loaded at last!\nHere are their dimensions (width,height):");
console.log(images.map(im=>([im.width,im.height])));
})
// Now, trigger the action:
images.forEach((im,i)=>im.src=`https://picsum.photos/id/${i+234}/200/100`);
<div><img><img></div>
Wele, loaded++ is inside onload.
That's probably why it doesn't work. It's not worth uploading images in order. Async programming will have a better effect than blocking.
async function create() {
var i=0
var arr = [{data:'1'},{data:'2'},{data:'3'},{data:'4'},{data:'5'}]
while(i<arr.length) {
console.log('create', arr[i])
await read(arr[i])
i++
}}
async function read(val) {
console.log('read',val)
}
create()
or
async function* reader() {
while (true) {
const value = yield;
console.log(value, '-----generator');
}
}
const read = reader();
var i =0
var arr = [{data:'1'},{data:'2'},{data:'3'},{data:'4'},{data:'5'}]
read.next(0) //first value not return
async function create() {
while(i<arr.length) {
await read.next(arr[i]);
console.log(arr[i], '-----loop');
i++
}
}
create()
Now do not e to that waiting it still performs in progress
Sorry for english i use translator
To wait for all images loaded (or error) I'll use this:
window.addEventListener('DOMContentLoaded', (event) => {
let imagesToLoad = [...document.images].filter(x => !x.plete);
if (imagesToLoad == 0) {
// All images loaded
} else {
imagesToLoad.forEach(imageToLoad => {
imageToLoad.onload = imageToLoad.onerror = () => {
if ([...document.images].every(x => x.plete)) {
// All images loaded
}
};
});
}
});
Another way, needs extra code:
window.addEventListener('DOMContentLoaded', (event) => {
Promise.progress(Array.from(document.images).filter(img => !img.plete).map(img => new Promise(resolve => { img.addEventListener('load', resolve); img.addEventListener('error', resolve); })), (p) => {
//showLoading(`Waiting for images on page to load.. (${p.loaded}/${p.total})`, 0, p.total, p.loaded);
}).then(() => {
// All images loaded
//hideLoading();
});
// Promise progress helper
Promise.progress = async function progress (iterable, onprogress) {
const promises = Array.from(iterable).map(this.resolve, this);
let resolved = 0;
const progress = increment => this.resolve(onprogress(new ProgressEvent('progress', { total: promises.length, loaded: resolved += increment })));
await this.resolve();
await progress(0);
return this.all(promises.map(promise => promise.finally(() => progress(1))));
}
});
A generic solution which checks for when all the images have loaded then calls function_after_image_is_loaded()
.
The problem with while loops is that it pauses the whole process, therefore the image loading process too. So, something like async, await, promises, must be involved. The following creates a promise which checks if all the images is loaded after around 10 milliseconds of its creation. For any given image element, if img.plete = true
, then properties like width
, clientWidth
, naturalWidth
, would be loaded. Then, if the images is not loaded yet, the promise will return a reject
, which will be handled by catch()
, and re-invoke the promise. The Math.random()
is necessary to make each Promise a new instance, so that it does not reuse the results from previous Promise and skip the 10 milliseconds interval. (Without Math.random()
, it can run thousands of times within a second, which will slow down the page.) And finally, when every image is loaded, it will return a resolve
, handled by then()
, and calls function_after_image_is_loaded()
, and stop re-invoking itself.
function wait_for_image_load_then(function_to_be_called) {
new Promise((resolve, reject) => {
setTimeout(
() => {
// selects all images, adjust according to need
images = document.querySelectorAll("img");
// checks if all image is pletey loaded
// returns true only if every element in the array is true
let all_plete = Array.from(images).every((img) => img.plete)
if (all_plete) {
resolve('images load finish');
}
else {
reject('image loading');
}
},
Math.random() + 10 // necessary to make each Promise a new instance
)
}).then(
// if resolve is returned in promise
(res) => { console.log(res); function_to_be_called(); }
).catch(
// if reject is returned in promise
(reason) => { console.log(reason); wait_for_image_load_then(function_to_be_called); }
);
}
function function_after_image_is_loaded() {
images = document.querySelectorAll("img");
Array.from(images).forEach((img) => console.log('width: ', img.width, '\theight: ', img.height));
}
wait_for_image_load_then(function_after_image_is_loaded);
本文标签: How to make Javascript wait for all images to load before proceedingStack Overflow
版权声明:本文标题:How to make Javascript wait for all images to load before proceeding? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1743899006a2558325.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论