...

View Full Version : Images not displayed when iterated



Luke7268
11-17-2012, 12:12 PM
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):



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:



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?

AndrewGSW
11-17-2012, 07:28 PM
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.

Luke7268
11-17-2012, 07:57 PM
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:



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...



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)

AndrewGSW
11-17-2012, 08:34 PM
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.


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.

Luke7268
11-17-2012, 10:05 PM
No luck yet, but:



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...

AndrewGSW
11-17-2012, 11:28 PM
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:


console.log(sourcepointer); // etc.

and examine this information within the browser's console (F12).

Disclaimer: I haven't done much work with canvas.

AndrewGSW
11-17-2012, 11:35 PM
You may need to remember the values for sourcepointer as well:


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.

Luke7268
11-17-2012, 11:45 PM
No luck I'm afraid...

I also tried:



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.

AndrewGSW
11-17-2012, 11:51 PM
My last attempt:


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];
}

Luke7268
11-18-2012, 12:06 AM
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:


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
11-18-2012, 01:26 AM
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?



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.

AndrewGSW
11-18-2012, 01:34 AM
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

var copyCanvas = function() {
isn't expecting to receive any data; that is, it has no parameter(s).

Luke7268
11-18-2012, 01:42 AM
I really appreciate all the hints.

However:



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
11-18-2012, 10:15 AM
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.



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
11-18-2012, 07:12 PM
This guy nails it down... http://www.impressivewebs.com/callback-functions-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:



<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.

Luke7268
11-18-2012, 07:22 PM
How do I mark this thread solved? Seems I can't edit my original title anymore.



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum