*Update with link to question I reference If I load multiple images with the for loop, do I only need one img.onload function?
I want to bring in many image files to use in canvas via an array of all the images (that way they load before I need them), Using a solution found on here I'm getting the error
"Uncaught TypeError: Cannot read property 'drawImage' of undefined
at Image. (app.js:32)" line 32 would be canvasContext[value].drawImage(images[i], 0, 0);
Obviously, I didn't understand the solution because I'm kind of at a loss as to what is causing this.
The end result would be for me to set up all the images on load and then in the draw function I have, declare their positions and sizing.
window.onload = function() {
console.log(keysDown);
canvas = document.getElementById('myCanvas');
canvasContext = canvas.getContext('2d');
var images = ['Frylock.png', 'Master_Shake.png'];
// Assign onload handler to each image in array
for (var i = 0; i <= images.length; i++) {
var img = new Image();
img.onload = (function(value) {
return function() {
canvasContext[value].drawImage(images[i], 0, 0);
}
})(i);
// IMPORTANT - Assign src last for IE
img.src = './' + images[i];
}
Related
I tried to build many konvajs canvas and transform them to konva images.
And finally load these images to another canvas and show.
function main(){
var total_konva_stage= createStage();
var imagesLayer= new Konva.Layer();
for (var key in array){
$(".tmpcontainer").remove();
containerName = containerNameBase + key;
$("#hidden_div").append("<div id='"+containerName+"' class='tmpcontainer'></div>");
var konva_stage =createcanvas(containerName); //create a hidden dynamic div and bind this stage to it
var img = konva_stage .toDataURL("image/png");
var imageObj = new Image();
imageObj.src = img
imageObj.onload = function() {
callback_canvastoImg(imagesLayer, imageObj);
};
}
total_konva_stage.add(imagesLayer);
total_konva_stage.show();
total_konva_stage.draw();
}
function callback_canvastoImg(imagesLayer, imageObj){
var konva_image = new Konva.Image({imageObj});
imagesLayer.add(konva_image );
}
What I think my results is that "total_konva_stage" will have every separate img that from konva_stage.
But I found my second img will contain the first img , my third img contain the first, second img
I have no idea now, please help.
Thanks
onload function callback is asynchronous. It means it will be executed only after the whole array is processed.
So in this line: callback_canvastoImg(imagesLayer, imageObj); variable imageObj will refer to the LAST created image.
To avoid this you can:
Use modern js syntax of declaring variables (in case you should use something like babel)
const imageObj = new Image();
You can define closure function for loop iteration. You just need to use array.forEach instead of for loop:
array.forEach(function(item, key) {
$(".tmpcontainer").remove();
containerName = containerNameBase + key;
$("#hidden_div").append("<div id='"+containerName+"' class='tmpcontainer'></div>");
var konva_stage =createcanvas(containerName); //create a hidden dynamic div and bind this stage to it
var img = konva_stage .toDataURL("image/png");
var imageObj = new Image();
imageObj.src = img
imageObj.onload = function() {
callback_canvastoImg(imagesLayer, imageObj);
};
}
function LoadResources(){
alert("In load socket");
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var tiles= new Array();
var loadedCount=0;
for (x = 101; x <= 155; x++) {
var imageObj = new Image(); // new instance for each image
imageObj.src = "Resources/ClassicCardImages/deck1/dood_deck/"+x+".GIF";
imageObj.onload=function(){
loadedCount++;
if(loadedCount==55){
cardsImagesLoaded();
}else {
alert(loadedCount);
}
};
tiles.push(imageObj);
}
};
So when i call the function LoadResources() it does give the alert "in load socket" but does not gives the alert while in imageObj.onload function.
You can use window.onload function i.e "window.onload = function() {..}" and my function in it while use in the body of html document.
Plus i m running it on Google chrome .Is there the problem with chrome's onload or something .
You are trying to increase the loading counter even if the onload function are called only once (at the moment when the images has been loaded completely), so there is no way to trigger the alert many times. For me it's not quite obvious what are you trying to do. Anyway if you want to load multiple images with onload function the best practice is to use a closure, otherwise on each iteration it may happens that at the end of the loop you will get only the last image loaded. I'm not going into detail into what a closure is, but the principle is something like this:
for (var i = 0; i< 4; i++) {
var imgObj = new Image();
imgObj.onload = (function(img) {
return function () {
ctx.drawImage(imgObj, 0, 0);
}
})(i);
imgObj.src = 'image.png';
}
This way by calling a new function you will create a new execution context retaining the value of i on each iteration.
I am just adding a little help for your code.
For assigning functions the way you're doing it , it is cheaper to do it the way below.
for (x = 101; x <= 155; x++) {
var imageObj = new Image(); // new instance for each image
imageObj.src = "Resources/ClassicCardImages/deck1/dood_deck/"+x+".GIF";
imageObj.onload= imageOnLoad;
}
function imageOnLoad(){
loadedCount++;
if(loadedCount==55){
cardsImagesLoaded();
}else {
alert(loadedCount);
}
};
tiles.push(imageObj);
}
So I am loading multiple images onto multiple canvases basically (one image per canvas/ctx - its a slideshow). I want to make sure that each image is loaded before it attempts to draw the image onto the canvas.
Here is the code...
In Example 1 i'm using 3 onload events (one for each image)
In Example 2 i'm using one onload event, but it is the last image that gets called in the for loop
Question: Can i use Example 2 and be confident to assume that if the last image is loaded, then the images before must be loaded as well?
Also---- I attempted this already, but can it all be done inside a for loop? I wasn't able to get it to work. See Example 3
Example 1 3 onload events
var img=[0,'slide1','slide2','slide3'];
for (var i=1;i<=3;i++) {
img[i] = new Image();
img[i].src = '/images/slide' + i + '.png'
}
img[1].onload = function() { // checks to make sure img1 is loaded
ctx[1].drawImage(img[1], 0, 0);
};
img[2].onload = function() { // checks to make sure img2 is loaded
ctx[2].drawImage(img[2], 0, 0);
};
img[3].onload = function() { // checks to make sure img3 is loaded
ctx[3].drawImage(img[3], 0, 0);
};
Example 2 only the last onload event
var img=[0,'slide1','slide2','slide3'];
for (var i=1;i<=3;i++) {
img[i] = new Image();
img[i].src = '/images/slide' + i + '.png'
}
img[3].onload = function() { // checks to make sure the last image is loaded
ctx[1].drawImage(img[1], 0, 0);
ctx[2].drawImage(img[2], 0, 0);
ctx[3].drawImage(img[3], 0, 0);
};
Example 3 one onload in the for loop to do all the onload events
var img=[0,'slide1','slide2','slide3'];
for (var i=1;i<=3;i++) {
img[i] = new Image();
img[i].src = '/images/slide' + i + '.png'
img[i].onload = function() { // checks to make sure all images are loaded
ctx[i].drawImage(img[i], 0, 0);
};
}
I assume I can't do example 3 because an image probably won't be loaded by the time the for loop runs the second time and rewrites the onload event for the next image thus erasing the onload event for the prior image. Correct?
You'll have scoping issues with the last for-loop because of closures. In order for something like this to work, you'll want to break out of the closure by encapsulating your functional assignment in it's own function. This article explains it well.
Ideally, your last for-loop would look something like this:
var images = [0,'slide1.png', 'slide2.png', 'slide3.png'];
// Assign onload handler to each image in array
for ( var i = 0; i <= images.length; i++ ){
var img = new Image();
img.onload = (function(value){
return function(){
ctx[value].drawImage(images[value], 0, 0);
}
})(i);
// IMPORTANT - Assign src last for IE
img.src = 'images/'+images[i];
}
Also, keep in mind that you'll need to assign the img.src attribute AFTER the onload handler has been defined for some flavors of IE. (this little tidbit cost me an hour of troubleshooting last week.)
I'm writing a simple 2D side scroller game using JavaScript + Canvas. I am trying to do this while learning OO JavaScript. I'm encountering an issue where my image will not draw to the canvas, despite being loaded correctly. Here's the code:
//===================================
// class - Canvas
//===================================
var classCanvas = function(id){
canvas = document.getElementById(id);
context = canvas.getContext("2d");
}
//===================================
// abstract class - Image Drawer
//===================================
var classImageDrawer = function(that){
this.draw = function(){
context.drawImage(that.img,
that.spriteCoordsX,
that.spriteCoordsY,
that.width,
that.height,
that.posX,
that.posY,
that.width,
that.height);
}
}
//===================================
// class - Level
//===================================
var classLevel = function(name, src){
this.name = name;
this.img = new Image();
this.img.src = src;
this.img.onload = function(){ };
this.spriteCoordsX = 0;
this.spriteCoordsY = 0;
this.posY = 0;
this.posX = 0;
this.height = 400;
this.width = 600;
this.drawer = new classImageDrawer(this);
}
//==================================
// Begin
//==================================
var game = new classCanvas("playground");
game.LevelOne = new classLevel("LevelOne", "images/foreground-land.png");
game.LevelOne.drawer.draw();
I have checked the code, and I know the image is getting loaded correctly. No errors occur during runtime, the image "foreground-land.png" simply DOESN'T show up on the canvas!
Your image may be loading fine, but as far as I can tell your call to game.LevelOne.drawer.draw() has no guarantee that foreground-land.png has loaded at that point (remember, images are loaded asynchronously without blocking the execution of other code).
Your onload function for the image object in your classLevel class is empty and anonymous. Nothing happens when the image is finished loading. I'm not sure how you want to structure your code given everything else you have going on, but you should only allow the call to game.LevelOne.drawer.draw() to succeed if the resources it depends upon have been fully loaded. The best way to do this would be to hook into the onload callback for all resources you require for that object (in this case, it's just the one image).
Loading images is an async operation, you are (basically) ignoring the loading process, and acting as though it is sync operations.
One thing you could look at is using a 3rd parameter as a callback ("LevelOne", "images/foreground-land.png", function() { this.drawer.draw() }) and doing that as the onload
I have this piece of code (The sCtx is a canvas context and the button is in a tight draw loop):
function Button(src, onClick)
{
this.loaded = false;
this.image = new Image();
this.image.src = src;
this.onClick = onClick;
}
Button.prototype.draw = function()
{
if(!this.image.complete)
return;
var theImg = this.image;
console.log(theImg);
sCtx.drawImage(theImg);
}
When I run the code (in Chrome) I get this output:
<img src="img/btnStart.png">
Uncaught TypeError: Type error
Can someone tell me what I am doing wrong? I have looked at many examples and it seems like this should work.
I believe you need x/y coordinates in order to tell the canvas context where to draw:
sCtx.drawImage(theImg,0,0);