dan2k3k4
dan2k3k4

Reputation: 1419

Image not drawn on canvas until user clicks?

I draw several images with a function that performs something similar to:

context.drawImage(img, width / 2 * (-1), height / 2 * (-1), width, height);

I've read that I need to wait for the image to be loaded before I can draw it, with something like this:

img.onload = function() {
    context.drawImage(img, width / 2 * (-1), height / 2 * (-1), width, height);
};

However this causes the image to be drawn but afterwards, nothing is drawn since I call up my draw function every few milliseconds as it's part of the 'animation' loop for a simple game.

Is there a way that I can wait for the onload event before I continue running code in my init() function?

I suppose something like:

var image_loaded = false;
img.onload = function() {
    image_loaded = true;
};
if(image_loaded) {
    animate();
}

Should work? Unless I need to add a timeOut function to keep calling the init() until image_loaded is true?

Upvotes: 4

Views: 1416

Answers (2)

Gustavo Carvalho
Gustavo Carvalho

Reputation: 2878

I created a simple and small library to make the load of images easy.

Check the demo

See the library code below:

// Simple Image Loader Library
window.Loader = (function () {
var imageCount = 0;
var loading = false;
var total = 0;

// this object will hold all image references
var images = {};

// user defined callback, called each time an image is loaded (if it is not defined the empty function wil be called)
function onProgressUpdate() {};
// user defined callback, called when all images are loaded (if it is not defined the empty function wil be called)
function onComplete() {};

function onLoadImage(name) {        
    ++imageCount;
    console.log(name + " loaded");

    // call the user defined callback when an image is loaded
    onProgressUpdate();

    // check if all images are loaded
    if (imageCount == total) {
        loading = false;
        console.log("Load complete.");
        onComplete();
    }

};

function onImageError(e) {
    console.log("Error on loading the image: " + e.srcElement);
}

function loadImage(name, src) {
    try {
        images[name] = new Image();
        images[name].onload = function () {
            onLoadImage(name);
        };
        images[name].onerror = onImageError;
        images[name].src = src;
    } catch (e) {
        console.log(e.message);
    }
}

function getImage(/**String*/ name){
    if(images[name]){
        return (images[name]);
   }
    else{
        return undefined; 
    }
}

// pre-load all the images and call the onComplete callback when all images are loaded
// optionaly set the onProgressUpdate callback to be called each time an image is loaded (useful for loading screens) 
function preload( /**Array*/ _images, /**Callback*/ _onComplete, /**Callback <optional>*/ _onProgressUpdate) {
    if (!loading) {

        console.log("Loading...");
        loading = true;

        try {
            total = _images.length;
            onProgressUpdate = _onProgressUpdate || (function(){});
            onComplete = _onComplete || (function(){});                

            for (var i = 0; i < _images.length; ++i) {
                loadImage(_images[i].name, _images[i].src);                    
            }
        } catch (e) {
            console.log(e.message);
        }
    } else {
        throw new Error("Acess denied: Cannot call the load function while there are remaining images to load.");
    }
}

// percentage of progress
function getProgress() {
    return (imageCount / total)*100;
};

// return only the public stuff to create our Loader object
return {
    preload: preload,
    getProgress: getProgress,
    getImage: getImage,
    images: images // have access to the array of images might be useful but is not necessary
};
})();

How it works

To make sure that images are loaded and they could be used by your application the library have the Loader.preload method.

The preload method will receive an array of objects, each object containing the name and the src properties of an image you want to load. Optionally you can setup the onComplete callback (to be called when all images are loaded) and the onProgressUpdate callback (to be called each time an image is loaded). The onProgressUpdate callback is useful if you want to create a loading screen for your application.

Use the Loader.getProgress() to obtain the percentage of images loaded at any time.

To obtain the reference of an image loaded, call Loader.getImage(name) where name is the name property (a String) of the image.

If you for some reason needs iterate over all images use Loader.images. It's the object containing all references for the images in its properties.

Use like this:

var images = [{name: "img1", src: "http://...a.."}, 
              {name: "img2", src: "http://...b.."},
              ...
              {name: "imgN", src: "http://...N.."}];

function onProgressUpdate(progress){
    ...
    drawProgressBar(progress); // just an example of what you can do
    ...
}

function onComplete(){
    ...        
    // get an Image from Loader object
    var texture = Loader.getImage("img2");
    // or use this:
    var texture = Loader.images.img2; // or still Loader.images["img2"]
    ...

    // iterate over the images
    for (var img in Loader.images){
        console.log(Loader.images[img].src);
    }
    ....
}

Loader.preload(images, onComplete, onProgressUpdate);

Check the demo if you didn't.

Upvotes: 3

Loktar
Loktar

Reputation: 35309

Live Demo

var imagesLoaded = [],
    images = [
        'http://www.zombiegames.net/inc/screenshots/The-Last-Stand-Union-City.png', 
    'http://www.zombiegames.net/inc/screenshots/Lab-of-the-Dead.png',
    'http://www.zombiegames.net/inc/screenshots/Zombotron.png',
    'http://www.zombiegames.net/inc/screenshots/Road-of-the-Dead.png'],
    canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d");

canvas.width = 640;
canvas.height = 480;

function init(){
    // just loops through the images.
    if(imagesLoaded.length > 0){
        ctx.drawImage(imagesLoaded[0], 0, 0, 640, 480);
        imagesLoaded.push(imagesLoaded.shift());   
    }
    setTimeout(init,500);
}

function preload(){
    for(var i = 0; i < images.length; i++){
        (function(value){
            return function(){
                var loadedImage = new Image();
                loadedImage.src = images[value];
                loadedImage.onload = function(){
                    imagesLoaded.push(loadedImage);  
                } 
            }();
        })(i);

    }
    checkLoaded();
}

function checkLoaded(){
    if(imagesLoaded.length === images.length){
        console.log(imagesLoaded.length);
        init(); 
    } else{
        setTimeout(checkLoaded,30);
    }
}

preload();

Above is an example of how to preload images and wait to do anything else. Basically what you do is have all your images in an array, and add the onload event to each of them. As they load I through them into another array that holds all the loaded images. Once the length of the two arrays match all of the images are ready to use.

Another way would be to increment a counter as they load and check its value against the length of the array. When the counter variable is equal to the length of the images array it means they've all loaded and are ready to use.

Upvotes: 4

Related Questions