33

I'm developing a game using JavaScript and canvas. As the game loads, all images that will be used are being cached.

Observing the resource timeline, I see that the following code triggers an asynchronous request:

var sprite = new Image();
sprite.src = "sprites/sheet1.png";

The engine will keep executing, eventually beginning to draw and play the level. Images that are loaded after the first frame is painted might never appear due to clipping (i.e. not getting "dirty").

So I tested the following:

console.log("begin");
var sprite = new Image();
sprite.onload = function() { console.log('loaded!'); };
sprite.src = "sprites/sheet1.png";
console.log("end");

The resulting console outputs in the order they occur are:

  • begin
  • end
  • loaded!

I'm looking for a similar way to $.ajax with async: false to perform the loading. Can't figure out how... thanks in advance for you help! J.

SharpC
  • 6,974
  • 4
  • 45
  • 40
Jem
  • 6,226
  • 14
  • 56
  • 74

3 Answers3

61

You shouldn't make anything synchronous (not even AJAX) calls but instead simply put your code in the appropriate callback:

function loadSprite(src, callback) {
    var sprite = new Image();
    sprite.onload = callback;
    sprite.src = src;
}

Then use it like this:

loadSprite('sprites/sheet1.png', function() {
    // code to be executed later
});

If you want to pass additional arguments, you can do it like this:

sprite.onload = function() {
    callback(whatever, args, you, have);
};

If you want to load multiple elements and need to wait for all of them to finish, consider using the jQuery deferred object:

function loadSprite(src) {
    var deferred = $.Deferred();
    var sprite = new Image();
    sprite.onload = function() {
        deferred.resolve();
    };
    sprite.src = src;
    return deferred.promise();
}

In the function loading the sprites, you do something like this:

var loaders = [];
loaders.push(loadSprite('1.png'));
loaders.push(loadSprite('2.png'));
loaders.push(loadSprite('3.png'));
$.when.apply(null, loaders).done(function() {
    // callback when everything was loaded
});

http://api.jquery.com/jQuery.when/

ThiefMaster
  • 310,957
  • 84
  • 592
  • 636
  • Thanks for your reply. I forgot to mention, this code is part of a function name "loadSprite(name)" that's being called by a parent. That parent calls it from a loop like "for each sprite loadSprite(name)". I need to be able to interrupt the processing, causing the method to return only when the image was loaded. Any idea? – Jem Dec 27 '11 at 13:39
  • You can't do this without freezing the browser. I'll update my answer to show a (the) proper solution. – ThiefMaster Dec 27 '11 at 13:41
  • Hey thanks, it's very interesting. I didn't know at all about that deferred object! – Jem Dec 27 '11 at 14:01
  • 1
    This is a great explanation on how the Deferred object can be used in a manner devoid of already deferred objects (like $.ajax) and not in conjunction with animations. Very useful and very well explained - thank you. – Brandt Solovij Apr 26 '12 at 22:46
2

This question is old, but there is a way to do this without requiring jquery, OR freezing the browser.

In image1.onLoad, make it load image2.
In image2.onLoad, make it load image3.
In image3.onLoad, make it load image4.
....
In imagen.onLoad make it load your main function.

I'm not sure if this is the entirely BEST way to do it, but it's better than freezing the browser, at least.
You are able to also load all of your audio files or whatever other resources you need, or any other javascript you need to run.

Freezing the browser is not required

  • 1
    This is how I am currently doing it (first night using Canvas) but it seems messy. Is there a better way of doing it? I've tried setting up true/false flags for resources loaded and using do/while loops but I can't get anything working other than this method. – BenM Nov 22 '13 at 22:49
  • 1
    Not sure. I only ever need one or two resources for my projects, so I never got into the whole resource loading scene. I'm sure there are better ways to do this. Best of luck to ya! :) – Interpolate Nov 26 '13 at 08:43
  • I ended up building a custom callback method that increased a resource counter (and compared it to the length of a resource array) and either did nothing if they were different values or began drawing onto the canvas if the values matched (which would mean all the resources had loaded). – BenM Nov 26 '13 at 09:42
0

I've this problem with canvas i tried your solution but a best and simple way that works is:

function COLPOinitCanvas(imagesfile) {
    COLPOcanvas = document.getElementById("COLPOcanvas");
    COLPOctx = COLPOcanvas.getContext("2d");
    var imageObj = new Image();
    imageObj.src = imagesfile;
    imageObj.onload = function () {
        COLPOctx.drawImage(imageObj, 0, 0, COLPOcanvas.width, COLPOcanvas.height);
    };
}
ettore ct
  • 199
  • 1
  • 4