简体   繁体   中英

Why does my image preloading method work?

My Question

The goal is to make sure all images are fully loaded before a new game can begin. 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 is what both of my fiddle solutions do to preload images:

  • There is an array of absolute image URLS for all of the assets required by the game
  • The init() function has a for loop which generates a new canvas Image() per URL in the array
  • Each newly created image is pushed into another array
  • So, we now have the first array containing URL strings, and a second array containing 'HTMLImageElement' objects

Fiddle B differs from Fiddle A, in that it utilises the '.onload' event, plus a counter. The two fiddles use different ways of checking to see if all the image assets have loaded:

Fiddle A: compares the length of the 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 second array length with the counter variable. This counter goes up by 1 every time an 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 Expanded

Fiddle A frequently (but not always) triggers the start of a new game before the full list of image assets has been properly loaded, causing game errors. Fiddle B consistently loads all of the image assets before allowing a new game to start. This can be seen in both fiddles from the 'Fully loaded' messages written to the canvas.

Although I can see that Fiddle A works better than Fiddle B, I don't understand why it is superior. Not all tutorials relating to loading HTMLImageElements use '.onload', and the tutorials that don't use it seem perfectly adequate.

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

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

Previous Research

Here are just a few examples of previous research I have done in order to try to answer my own question.

  • An article on pre-loading images which doesn't have any reference to an .onload event
  • The accepted solution to this question does not use .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, the explanation of the solution hasn't helped me to answer my own question.

You are comparing two very different approaches: sequential with for (which is incorrect) and event-based.

Image downloading is asynchronous process so when the image src property is set the browser starts downloading it. It can be, however, very fast especially if the image was already had been downloaded by the browser and cached internally (in fact, it is blazingly fast). So 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 the cache or use incognito mode and download them from the remote location (not your local server) then boom! - the loop ends with no image downloaded at all.

Another approach slightly better but the game is set up for every image downloaded, which is probably do 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 if one of the images does not exist, the game never starts so you need to workaround it with timeout.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM