I am working on a dynamic canvas animation that will load and animate multiple images.
Seems I've run into a race condition, and I don't know how to resolve it.
This object constructor loads the images, creates objects for them, and sets width and height variables as 1/2 of the natural image size.
window.imgWidth;
window.imgHeight;
//array to hold character objects
window.character = [];
window.characterPosition;
function Character(name, x, y){
//define the image object within the Character
this.imageObject = new Image();
this.imageObject.src = 'data:text/javascript;base64,'+name;
window.character.push(this);
window.characterPosition = window.character.indexOf(this);
//set natural width and natural height once the image is loaded
//conditional used by Chrome
if (this.imageObject.addEventListener){
this.imageObject.addEventListener('load', function(){
window.imgWidth = this.naturalWidth/2;
window.imgHeight = this.naturalHeight/2;
//set natural width and natural height to object
window.character[characterPosition]['imageObject']['w'] = window.character[characterPosition]['imageObject']['w0'] = window.imgWidth;
window.character[characterPosition]['imageObject']['h'] = window.character[characterPosition]['imageObject']['h0'] = window.imgHeight;
//set initial x and y position
window.character[characterPosition]['imageObject']['x1'] = x;
window.character[characterPosition]['imageObject']['y1'] = y;
//set loaded property for the object once loading is done
window.character[characterPosition]['imageObject']['loaded'] = true;
console.log(characterPosition);
console.log(window.character[characterPosition]['imageObject']);
function imageLoaded(element, index, array){
return element['imageObject']['loaded'] == true;
}
//test whether every object in array has the image loaded
if(character.every(imageLoaded)){
$('button#play').show();
};
});
}
} //end object constructor
Inside document ready, I am using this object constructor to create two objects.
var sun0 = new Character('iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAW0lEQVR42mL8//8/AzpgZGTcC6KBcs5wMRwK/0MVMsLEmLAoEmXAApiwiKUhaRJCltgLsQVsWwIQ/wTx0fBeRigD7B6Y24i1mj4Kn4KI7Uie2Y7FI8+B2AMgwABjRynfWgpcxQAAAABJRU5ErkJggg==', 12, 12);
var sun1 = new Character('R0lGODlhCgAKANUCAEKtP0StQf8AAG2/a97w3qbYpd/x3mu/aajZp/b79vT69MnnyK7crXTDcqraqcfmxtLr0VG0T0ivRpbRlF24Wr7jveHy4Pv9+53UnPn8+cjnx4LIgNfu1v///37HfKfZpq/crmG6XgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAIALAAAAAAKAAoAAAZIQIGAUDgMEASh4BEANAGAxRAaaHoYAAPCCZUoOIDPAdCAQhIRgJGiAG0uE+igAMB0MhYoAFmtJEJcBgILVU8BGkpEAwMOggJBADs=',0,0);
The problem is that since this.imageObject.addEventListener('load', function(){
is asynchronous, the console.log
messages that run each time the object constructor is run, both display information from the second object. I want the first set to display information from the first object and the second set to display information from the second object.
//First set, run by sun0
console.log(characterPosition); //1
console.log(window.character[characterPosition]['imageObject']); //info for sun1
//Second set, run by sun1
console.log(characterPosition); //1
console.log(window.character[characterPosition]['imageObject']); //info for sun1
I was able to work around it by setting a timer like so:
var sun0 = new Character('iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAW0lEQVR42mL8//8/AzpgZGTcC6KBcs5wMRwK/0MVMsLEmLAoEmXAApiwiKUhaRJCltgLsQVsWwIQ/wTx0fBeRigD7B6Y24i1mj4Kn4KI7Uie2Y7FI8+B2AMgwABjRynfWgpcxQAAAABJRU5ErkJggg==', 12, 12);
window.setTimeout(slowLoad, 2000);
function slowLoad(){
var sun1 = new Character('R0lGODlhCgAKANUCAEKtP0StQf8AAG2/a97w3qbYpd/x3mu/aajZp/b79vT69MnnyK7crXTDcqraqcfmxtLr0VG0T0ivRpbRlF24Wr7jveHy4Pv9+53UnPn8+cjnx4LIgNfu1v///37HfKfZpq/crmG6XgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAIALAAAAAAKAAoAAAZIQIGAUDgMEASh4BEANAGAxRAaaHoYAAPCCZUoOIDPAdCAQhIRgJGiAG0uE+igAMB0MhYoAFmtJEJcBgILVU8BGkpEAwMOggJBADs=',0,0);
}
This worked as expected.
//First set, run by sun0
console.log(characterPosition); //0
console.log(window.character[characterPosition]['imageObject']); //info for sun0
//Second set, run by sun1
console.log(characterPosition); //1
console.log(window.character[characterPosition]['imageObject']); //info for sun1
But obviously, I don't want to introduce a manual delay in order to get the function for sun1 to run only after sun0 completes. How can I get var sun1
to run immediately after sun0 finishes its function (including the asynchronous part)? Keep in mind that I will be doing this for several (about 15) other images as well.