Why does my method of preloading images work?

My question

The goal is to have all images fully loaded before starting a new game. My second solution (Fiddle B) achieves this goal more consistently and accurately than my first solution (Fiddle A). Why?

JSFIDDLE A

JSFIDDLE B

Methods

Here's what both of our violin solutions do for image preloading:

  • There is an array of absolute image urls for all of the assets needed to play the game.
  • The init () function has a for loop that generates a new Image () for the URL in the array
  • Each newly created image is placed in a different array
  • So now we have a first array containing URL strings and a second array containing "HTMLImageElement" objects

Fiddle B differs from Fiddle A in that it uses a .onload event plus a counter. The two scripts use different methods to check if all images are loaded:

Fiddle A: Compares the length of two arrays. If they match, start the game

for (var i = 0; i < allGameImageUrls.length; i++) {
        var img = new Image();
        img.src = allGameImageUrls[i];
        allGameImages.push(img);
        console.log(allGameImages.length);
        if (allGameImages.length >= allGameImageUrls.length) {
            setUpGame();
        } else {
            // images haven't loaded yet
            console.log('STILL LOADING');
            STAGE.fillText("Loading ...", 20, 400);
        }
    }

      

Fiddle B: Compares the length of the second array to a counter variable. This counter is incremented by 1 each time the image's ".onload" event completes.

for (var i = 0; i < allGameImageUrls.length; i++) {
    var img = new Image();
    img.onload = function () {
        assetCount++;
        console.log('assetCount = ' + assetCount);
        setUpGame();
    };
    img.src = allGameImageUrls[i];
    allGameImages.push(img);
}

      

My question is advanced

Fiddle Often (but not always) launches a new game before the full list of image objects was loaded correctly, which caused game errors. Fiddle B loads all images sequentially before allowing a new game to start. This can be seen in both scripts from the "Fully loaded" messages written to the canvas.

While I can see that Fiddle A performs better than Fiddle B, I don't understand why it is superior. Not all tutorials related to loading HTMLImageElements use ".onload", and tutorials that do not use it seem to be adequate.

Also, I don't understand why comparing the lengths of the two arrays is not as accurate as comparing the length of the second array with a counter.

I understand how the two solutions differ from each other, but I want to know why solution B works better than solution A.

Previous research

Here are some examples of previous research I've done to try and answer my own question.

  • An article about preloading images that has no .onload event link
  • The accepted solution to this question does not use the .onload event, but it still works
  • The accepted solution to this other question is very similar to my Fiddle B (although I discovered it much later). However, explaining this solution did not help me answer my own question.
+3


source to share


1 answer


You are comparing two very different approaches: sequential with for

(which is wrong) and event-based.

Loading images is an asynchronous process, so when the image property is src

set, the browser starts loading it. This can be very fast, however, especially if the image has already been loaded by the browser and cached internally (in fact, it's incredibly fast). Therefore, when the next iteration starts, it is already available (or at least almost all of them are available at the end of the loop). But if you clear your cache or use incognito mode and download them from a remote location (not your local server), then boom! - the loop ends without loading the image at all.

The other approach is slightly better, but the game is tuned for each image loaded, which is probably not what is required.



Consider the following approach:

var length = allGameImageUrls.length,
    count = 0;

var i, img;

for (i = 0; i < length; i++) {
    img = new Image();
    img.onload = function () {
        count++;
        // count is increased on every callback
        // so if number of executed callbacks equals
        // the number of images then all the images
        // are downloaded
        if (count === length) {
            setUpGame();
        }
    };
    img.src = allGameImageUrls[i];
    allGameImages.push(img);
} 

      

The only drawback is that if one of the images doesn't exist, the game never starts, so you have to bypass it with a timeout.

+1


source







All Articles