Go Back   CodingForums.com > :: Client side development > JavaScript programming

Before you post, read our: Rules & Posting Guidelines

Reply
 
Thread Tools Rate Thread
Enjoy an ad free experience by logging in. Not a member yet? Register.
Old 11-17-2012, 12:12 PM   PM User | #1
Luke7268
New Coder

 
Join Date: Nov 2012
Posts: 10
Thanks: 6
Thanked 0 Times in 0 Posts
Luke7268 is an unknown quantity at this point
Images not displayed when iterated

Hi all,

I'm not a professional programmer and fairly new to JavaScript, so please don't laugh. Here goes nothing.

I'm trying to display 10 images pulled from a database using PHP/MySQL next to eachother (to use as source canvas for a scrolling routine in another canvas).

This works (assume all variables and arrays have been defined properly - I'm merely showing the important part):

Code:
img[0] = new Image();
img[0].onload = function() {
 		ctxsrc.drawImage(img[0], sourcepointer, 0, width[0], height);
		sourcepointer=sourcepointer+width[0];
};
img[0].src = imgsrc[0];

img[1] = new Image();
img[1].onload = function() {
 		ctxsrc.drawImage(img[1], sourcepointer, 0, width[1], height);
		sourcepointer=sourcepointer+width[1];
};
img[1].src = imgsrc[1];
But this gives a Type Error in the drawImage line:

Code:
for (i=0; i<number; i++) {
	img[i] = new Image();
	img[i].onload = function() {
 		ctxsrc.drawImage(img[i], sourcepointer, 0, width[i], height);
		sourcepointer=sourcepointer+width[i];
	};
	img[i].src = imgsrc[i];
}
I'm baffled! How come?

Last edited by Luke7268; 11-17-2012 at 12:18 PM..
Luke7268 is offline   Reply With Quote
Old 11-17-2012, 07:28 PM   PM User | #2
AndrewGSW
Senior Coder

 
Join Date: Apr 2011
Location: London, England
Posts: 2,120
Thanks: 15
Thanked 354 Times in 353 Posts
AndrewGSW will become famous soon enough
Number is a keyword (or type) in JS so I wouldn't use that as a variable name.

So width is an array but height isn't? Personally, I would not use width or height as identifiers either.

check that width[i] is a number, rather than a string or something else, so that it can be ADDED to sourcepointer.
__________________
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
Validate your HTML and CSS

Last edited by AndrewGSW; 11-17-2012 at 07:31 PM..
AndrewGSW is offline   Reply With Quote
Users who have thanked AndrewGSW for this post:
Luke7268 (11-18-2012)
Old 11-17-2012, 07:57 PM   PM User | #3
Luke7268
New Coder

 
Join Date: Nov 2012
Posts: 10
Thanks: 6
Thanked 0 Times in 0 Posts
Luke7268 is an unknown quantity at this point
Thank you for your reply so far!

There's no array for height as the height of all pictures is the same (they are automatically resized to the canvas height, while preserving the aspect ratio).

Unfortunately changing the code into this:

Code:
for (i=0; i<numberofpics; i++) {
	img[i] = new Image();
	img[i].onload = function() {
 		ctxsrc.drawImage(img[i], sourcepointer, 0, widthpic[i], heightcanvas);
		sourcepointer=sourcepointer+widthpic[i];
	};
	img[i].src = imgsrc[i];
}
...gives the very same issue.

widthpic definitely contains a value, as...

Code:
img[0] = new Image();
img[0].onload = function() {
 		ctxsrc.drawImage(img[0], sourcepointer, 0, widthpic[0], heightcanvas);
		sourcepointer=sourcepointer+widthpic[0];
};
img[0].src = imgsrc[0];

img[1] = new Image();
img[1].onload = function() {
 		ctxsrc.drawImage(img[1], sourcepointer, 0, widthpic[1], heightcanvas);
		sourcepointer=sourcepointer+widthpic[1];
};
img[1].src = imgsrc[1];

for (i=0; i<numberofpics; i++) {
	img[i] = new Image();
	img[i].onload = function() {
 		ctxsrc.drawImage(img[i], sourcepointer, 0, widthpic[i], heightcanvas);
		sourcepointer=sourcepointer+widthpic[i];
	};
	img[i].src = imgsrc[i];
}
...shows. This displays two pictures. The loop however generates an error.

ctxsrc.drawImage(img[i], sourcepointer, 0, widthpic[i], heightcanvas); <= Uncaught TypeError: Type error (repeated 9 times)

Last edited by Luke7268; 11-17-2012 at 08:04 PM.. Reason: Mistake in code
Luke7268 is offline   Reply With Quote
Old 11-17-2012, 08:34 PM   PM User | #4
AndrewGSW
Senior Coder

 
Join Date: Apr 2011
Location: London, England
Posts: 2,120
Thanks: 15
Thanked 354 Times in 353 Posts
AndrewGSW will become famous soon enough
I think this is maybe a closure issue. the onload events don't fire until all the loops have finished, in which case i is 9 (or 10) for all of the images.

Code:
for (i=0; i<numberofpics; i++) {
	img[i] = new Image();
	img[i].onload = function() {
                var keepi = i;
 		ctxsrc.drawImage(img[keepi], sourcepointer, 0, widthpic[keepi], heightcanvas);
		sourcepointer=sourcepointer+widthpic[keepi];
	};
	img[i].src = imgsrc[i];
}
Otherwise, I would try setting the .src before assigning the onload events.
__________________
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
Validate your HTML and CSS
AndrewGSW is offline   Reply With Quote
Users who have thanked AndrewGSW for this post:
Luke7268 (11-18-2012)
Old 11-17-2012, 10:05 PM   PM User | #5
Luke7268
New Coder

 
Join Date: Nov 2012
Posts: 10
Thanks: 6
Thanked 0 Times in 0 Posts
Luke7268 is an unknown quantity at this point
No luck yet, but:

Code:
for (i=0; i<numberofpics; i++) {
	img[i] = new Image();
	img[i].src = imgsrc[i];
	img[i].onload = function() {
		var keepi = i;
 		ctxsrc.drawImage(img[keepi], sourcepointer, 0, widthpic[keepi], heightcanvas);
		sourcepointer=sourcepointer+widthpic[keepi];
	};
}
...gives only 6 (?!) errors. There's definitely something funny going on, and it seems that it has to do with the loading times...
Luke7268 is offline   Reply With Quote
Old 11-17-2012, 11:28 PM   PM User | #6
AndrewGSW
Senior Coder

 
Join Date: Apr 2011
Location: London, England
Posts: 2,120
Thanks: 15
Thanked 354 Times in 353 Posts
AndrewGSW will become famous soon enough
If it is related to loading times then try reducing the number of images temporarily to see if it behaves better. If this is the case then creating timeouts may not help as it (I'm assuming) needs to wait until the first drawImage completes before starting the second, etc..

Hopefully this is not the issue as it may prove tricky to stagger the image-drawing.

Try outputting the variable information each time within the loop:

Code:
console.log(sourcepointer); // etc.
and examine this information within the browser's console (F12).

Disclaimer: I haven't done much work with canvas.
__________________
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
Validate your HTML and CSS
AndrewGSW is offline   Reply With Quote
Users who have thanked AndrewGSW for this post:
Luke7268 (11-18-2012)
Old 11-17-2012, 11:35 PM   PM User | #7
AndrewGSW
Senior Coder

 
Join Date: Apr 2011
Location: London, England
Posts: 2,120
Thanks: 15
Thanked 354 Times in 353 Posts
AndrewGSW will become famous soon enough
You may need to remember the values for sourcepointer as well:

Code:
for (i=0; i<numberofpics; i++) {
	img[i] = new Image();
	img[i].onload = function() {
                var keepi = i, keepsp = sourcepointer;
 		ctxsrc.drawImage(img[keepi], keepsp, 0, widthpic[keepi], heightcanvas);
		sourcepointer=sourcepointer+widthpic[keepi];
	};
	img[i].src = imgsrc[i];
}
otherwise it might be trying to draw things outside of the canvas.
__________________
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
Validate your HTML and CSS
AndrewGSW is offline   Reply With Quote
Users who have thanked AndrewGSW for this post:
Luke7268 (11-18-2012)
Old 11-17-2012, 11:45 PM   PM User | #8
Luke7268
New Coder

 
Join Date: Nov 2012
Posts: 10
Thanks: 6
Thanked 0 Times in 0 Posts
Luke7268 is an unknown quantity at this point
No luck I'm afraid...

I also tried:

Code:
for (i=0; i<numberofpics; i++) {
	img[i] = new Image();
	img[i].onload = function() {
                var keepi = i, keepsp = sourcepointer;
 		ctxsrc.drawImage(img[keepi], keepsp, 0, widthpic[keepi], heightcanvas);
		keepsp =keepsp +widthpic[keepi];
	};
	img[i].src = imgsrc[i];
}
...but that didn't work either.
Luke7268 is offline   Reply With Quote
Old 11-17-2012, 11:51 PM   PM User | #9
AndrewGSW
Senior Coder

 
Join Date: Apr 2011
Location: London, England
Posts: 2,120
Thanks: 15
Thanked 354 Times in 353 Posts
AndrewGSW will become famous soon enough
My last attempt:

Code:
for (i=0; i<numberofpics; i++) {
	img[i] = new Image();
	img[i].onload = function() {
                var keepi = i, keepsp = sourcepointer;
 		ctxsrc.drawImage(img[keepi], keepsp, 0, widthpic[keepi], heightcanvas);
	};
        sourcepointer = sourcepointer + widthpic[i];
	img[i].src = imgsrc[i];
}
__________________
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
Validate your HTML and CSS
AndrewGSW is offline   Reply With Quote
Users who have thanked AndrewGSW for this post:
Luke7268 (11-18-2012)
Old 11-18-2012, 12:06 AM   PM User | #10
Luke7268
New Coder

 
Join Date: Nov 2012
Posts: 10
Thanks: 6
Thanked 0 Times in 0 Posts
Luke7268 is an unknown quantity at this point
I read a bit about asynchronous script execution and functions in functions. Then I read about callback hell. I read and read, but the whole concept of functions inside functions was making me dizzy.

Until I found this page:
http://callbackhell.com/

I still didn't understand half of it, but one line hit me: "Write small modules that each do one thing, and assemble them into other modules that do a bigger thing. You can't get into callback hell if you don't go there."

Then I came up with this:
Code:
var loadPictureOnCanvas = function (index, url, sourcepointer, widthpic, heightcanvas) {
	img[index] = new Image();
	img[index].onload = function() {
 		ctxsrc.drawImage(img[index], sourcepointer, 0, widthpic, heightcanvas);
	};
	img[index].src = url;
}

for (i=0; i<numberofpics; i++) {
	loadPictureOnCanvas(i, imgsrc[i], sourcepointer, widthpic[i], heightcanvas);
	sourcepointer=sourcepointer+widthpic[i];
}
It works like a charm, and is so much easier to understand than functions inside functions, a concept which I really can't get my head around yet, especially not when people start nesting them infinitely.
Luke7268 is offline   Reply With Quote
Old 11-18-2012, 01:26 AM   PM User | #11
Luke7268
New Coder

 
Join Date: Nov 2012
Posts: 10
Thanks: 6
Thanked 0 Times in 0 Posts
Luke7268 is an unknown quantity at this point
This script works a lot better, as it uses callback (I think) in a way I think I understand. I've tried some more convoluting syntaxes (I believe this is called callback hell), but I can't get my head around them.

This loads and displays all 10 images fine. However, the copy to the new canvas fails because JS is trying to copy the images from the source canvas while they are not displayed yet. However, all of them have neatly been preloaded, as the copying function is not executed unless the source canvas is ready and contains all pictures.

When I insert alerts though, the source canvas clearly shows that the pictures display there *after* the (still empty) source canvas has been copied to the target canvas. Is there some time between loading and displaying? Apparently. If so, how do I check on that?

Code:
var loadPictureOnCanvas = function (index, url, sourcepointer, widthpic, heightcanvas) {
	img[index] = new Image();
	img[index].onload = function() {
 		contextsrc.drawImage(img[index], sourcepointer, 0, widthpic, heightcanvas);
	};
	img[index].src = url;
	alert(index);
}

var copyCanvas = function() {
	contextdest.drawImage(canvassrc, 0, 0, widthcanvas, heightcanvas, 0, 0, widthcanvas, heightcanvas);
	alert('thenCopy');
}

var makeSourceCanvas = function() {
	for (i=0; i<numberofpics; i++) {
		loadPictureOnCanvas(i, imgsrc[i], sourcepointer, widthpic[i], heightcanvas);
		sourcepointer=sourcepointer+widthpic[i];
	}
	alert('makeSource');
}

copyCanvas(makeSourceCanvas());
The alerts give 0 1 2 3 4 5 6 7 8 9 makeSource thenCopy... but only after I click thenCopy away do the pictures display in the source canvas. Which explains why the target canvas stays empty.

Last edited by Luke7268; 11-18-2012 at 01:32 AM..
Luke7268 is offline   Reply With Quote
Old 11-18-2012, 01:34 AM   PM User | #12
AndrewGSW
Senior Coder

 
Join Date: Apr 2011
Location: London, England
Posts: 2,120
Thanks: 15
Thanked 354 Times in 353 Posts
AndrewGSW will become famous soon enough
Code:
copyCanvas(makeSourceCanvas());
// this passes the result of the function makeSourceCanvas to your function copyCanvas, but makeSourceCanvas doesn't return a result (other than null or undefined - the default).

..but your function
Code:
var copyCanvas = function() {
isn't expecting to receive any data; that is, it has no parameter(s).
__________________
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
Validate your HTML and CSS
AndrewGSW is offline   Reply With Quote
Users who have thanked AndrewGSW for this post:
Luke7268 (11-18-2012)
Old 11-18-2012, 01:42 AM   PM User | #13
Luke7268
New Coder

 
Join Date: Nov 2012
Posts: 10
Thanks: 6
Thanked 0 Times in 0 Posts
Luke7268 is an unknown quantity at this point
I really appreciate all the hints.

However:

Code:
var loadPictureOnCanvas = function (index, url, sourcepointer, widthpic, heightcanvas) {
	img[index] = new Image();
	img[index].onload = function() {
 		contextsrc.drawImage(img[index], sourcepointer, 0, widthpic, heightcanvas);
	};
	img[index].src = url;
	alert(index);
}

var copyCanvas = function(callback) {
	contextdest.drawImage(canvassrc, 0, 0, widthcanvas, heightcanvas, 0, 0, widthcanvas, heightcanvas);
	alert('thenCopy');
	return callback;
}

var makeSourceCanvas = function(callback) {
	for (i=0; i<numberofpics; i++) {
		loadPictureOnCanvas(i, imgsrc[i], sourcepointer, widthpic[i], heightcanvas);
		sourcepointer=sourcepointer+widthpic[i];
	}
	alert('makeSource');
	return callback;
}

copyCanvas(makeSourceCanvas(callback));
...gives exactly the same issue.

Now I have never worked with asynchronous code before, so I may be entirely missing the purpose of callbacks... Apparently I do, else this would have worked.
Luke7268 is offline   Reply With Quote
Old 11-18-2012, 10:15 AM   PM User | #14
Luke7268
New Coder

 
Join Date: Nov 2012
Posts: 10
Thanks: 6
Thanked 0 Times in 0 Posts
Luke7268 is an unknown quantity at this point
As I'm scrolling pixel by pixel anyway I just gave up on the whole thing and decided to just start scrolling whether the source canvas' contents have been copied or not. In a worst-case-scenario, I miss two pixels or so, but this is not detectable by the human eye.

Code:
window.requestAnimFrame = (function(callback) {
	return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
	function(callback) {
		window.setTimeout(callback, 1000 / 60);
	};
})();
   
var heightcanvas = $heightcanvas;
var widthcanvas =  $widthcanvas;
var topleft = widthcanvas;
var numberofpics = $zoekfotosaantal;
var totalwidth = $totalwidth;

var imgsrc = new Array();
var widthpic = new Array();
var img = new Array();

imgsrc[0] = '$fotourl[0]';
widthpic[0] = $widthnew[0];
	
imgsrc[1] = '$fotourl[1]';
widthpic[1] = $widthnew[1];
	
imgsrc[2] = '$fotourl[2]';
widthpic[2] = $widthnew[2];
	
imgsrc[3] = '$fotourl[3]';
widthpic[3] = $widthnew[3];
	
imgsrc[4] = '$fotourl[4]';
widthpic[4] = $widthnew[4];

imgsrc[5] = '$fotourl[5]';
widthpic[5] = $widthnew[5];
	
imgsrc[6] = '$fotourl[6]';
widthpic[6] = $widthnew[6];
	
imgsrc[7] = '$fotourl[7]';
widthpic[7] = $widthnew[7];
	
imgsrc[8] = '$fotourl[8]';
widthpic[8] = $widthnew[8];

imgsrc[9] = '$fotourl[9]';
widthpic[9] = $widthnew[9];
	
var canvasdest = document.getElementById('myCanvasDest');
var contextdest = canvasdest.getContext('2d');
contextdest.fillStyle = '#000000'; 
contextdest.fillRect(0, 0, widthcanvas, heightcanvas);  

var canvassrc = document.getElementById('myCanvasSrc');
var contextsrc = canvassrc.getContext('2d');
contextsrc.fillStyle = '#000000'; 
contextsrc.fillRect(0, 0, totalwidth, heightcanvas);

var sourcepointer = 0;
var scrollpointer = 0;
var i = 0;

var loadPictureOnCanvas = function (index, url, sourcepointer, widthpic, heightcanvas) {
	img[index] = new Image();
	img[index].onload = function() {
 		contextsrc.drawImage(img[index], sourcepointer, 0, widthpic, heightcanvas);
	};
	img[index].src = url;
	// if (index==numberofpics-1) {
	// 	alert(index);
	// }
}

var scroll = function() {
	contextdest.drawImage(canvassrc, scrollpointer, 0, 1, heightcanvas, widthcanvas-1, 0, 1, heightcanvas);
	contextdest.drawImage(canvasdest, 1, 0, widthcanvas-1, heightcanvas, 0, 0, widthcanvas-1, heightcanvas);
	// alert('thenCopy');
	// request new frame
	requestAnimFrame(function() {
		scroll();
	});
	scrollpointer++;
	if (scrollpointer>totalwidth) {
		scrollpointer=0;
	}
}

var makeSourceCanvas = function() {
	for (i=0; i<numberofpics; i++) {
		loadPictureOnCanvas(i, imgsrc[i], sourcepointer, widthpic[i], heightcanvas);
		sourcepointer=sourcepointer+widthpic[i];
	}
	// alert('makeSource');
}

makeSourceCanvas();
scroll(); // I still have no idea to start this *after* makeSourceCanvas is completely finished
Still curious after a solution for starting the scroll *after* makeSourceCanvas is done...

See the script in action at www.japanology.nl (top left corner).
Luke7268 is offline   Reply With Quote
Old 11-18-2012, 07:12 PM   PM User | #15
Luke7268
New Coder

 
Join Date: Nov 2012
Posts: 10
Thanks: 6
Thanked 0 Times in 0 Posts
Luke7268 is an unknown quantity at this point
Solution found. This guy nails it down...

This guy nails it down... http://www.impressivewebs.com/callba...ns-javascript/

The first proper explanation about callback I see on the web that can actually be understood by stupid beginners like me. I have finally seen the light.

After that, solving the problem was pretty easy:

Code:
<script>
window.requestAnimFrame = (function(callback) {
	return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
	function(callback) {
		window.setTimeout(callback, 1000 / 60);
	};
})();
   
var heightcanvas = $heightcanvas;
var widthcanvas =  $widthcanvas;
var topleft = widthcanvas;
var numberofpics = $zoekfotosaantal;
var totalwidth = $totalwidth;

var imgsrc = new Array();
var widthpic = new Array();
var img = new Array();

imgsrc[0] = '$fotourl[0]';
widthpic[0] = $widthnew[0];
	
imgsrc[1] = '$fotourl[1]';
widthpic[1] = $widthnew[1];
	
imgsrc[2] = '$fotourl[2]';
widthpic[2] = $widthnew[2];
	
imgsrc[3] = '$fotourl[3]';
widthpic[3] = $widthnew[3];
	
imgsrc[4] = '$fotourl[4]';
widthpic[4] = $widthnew[4];

imgsrc[5] = '$fotourl[5]';
widthpic[5] = $widthnew[5];
	
imgsrc[6] = '$fotourl[6]';
widthpic[6] = $widthnew[6];
	
imgsrc[7] = '$fotourl[7]';
widthpic[7] = $widthnew[7];
	
imgsrc[8] = '$fotourl[8]';
widthpic[8] = $widthnew[8];

imgsrc[9] = '$fotourl[9]';
widthpic[9] = $widthnew[9];
	
var canvasdest = document.getElementById('myCanvasDest');
var contextdest = canvasdest.getContext('2d');
contextdest.fillStyle = '#000000'; 
contextdest.fillRect(0, 0, widthcanvas, heightcanvas);  

var canvassrc = document.getElementById('myCanvasSrc');
var contextsrc = canvassrc.getContext('2d');
contextsrc.fillStyle = '#000000'; 
contextsrc.fillRect(0, 0, totalwidth, heightcanvas);

var sourcepointer = 0;
var scrollpointer = 0;
var i = 0;

var loadPictureOnCanvas = function (index, url, sourcepointer, widthpic, heightcanvas) {
img[index] = new Image();
img[index].onload = function() {
contextsrc.drawImage(img[index], sourcepointer, 0, widthpic, heightcanvas);
	if (index==9) {
		copyCanvas();
	}
};
img[index].src = url;
alert(index);
}

var copyCanvas = function() {
contextdest.drawImage(canvassrc, 0, 0, widthcanvas, heightcanvas, 0, 0, widthcanvas, heightcanvas);
alert('thenCopy');
}

var makeSourceCanvas = function() {
for (i=0; i<numberofpics; i++) {
loadPictureOnCanvas(i, imgsrc[i], sourcepointer, widthpic[i], heightcanvas);
sourcepointer=sourcepointer+widthpic[i];
}
alert('makeSource');
}

makeSourceCanvas();

</script>
The magic happens in loadPictureOnCanvas, specifically within the onload method.

Last edited by Luke7268; 11-18-2012 at 07:19 PM..
Luke7268 is offline   Reply With Quote
Reply

Bookmarks

Jump To Top of Thread


Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 08:06 PM.


Advertisement
Log in to turn off these ads.