execute function after the previous one is done - javascript

I'm trying to execute function after anotherfunction but I'm getting this error in chrome:
TypeError: Cannot read property 'done' of undefined
The functions look like this but I guess it's not so important because they works fine:
function loadImages(){
var img_split = new Image();
img_split.onload = function(){
var this_canvas = img_canvas_split;
this_canvas.width = w;
this_canvas.height = h;
this_canvas.getContext('2d').drawImage(this, 0,0, w, h);
console.log("img 2");
};
img_split.src = path;
var bg = new Image();
bg.onload = function(){
var this_canvas = bg_canvas;
this_canvas.width = panorama_w;
this_canvas.height = panorama_h;
this_canvas.getContext('2d').drawImage(this, panoramaX,panoramaY, panorama_w, panorama_h);
console.log("img 3");
};
bg.src = path_panorama;
}
var drawToMain = function(){
...
main_ctx.drawImage( bg_b, 0, 0, bg_b.width * 0.5, bg_b.height * 0.5, panoramaX, panoramaY, bg_canvas.width, bg_canvas.height );
main_ctx.translate(imageX+img_canvas.width/2, imageY+img_canvas.height/2);
main_ctx.rotate(angle);
main_ctx.translate(-(imageX+img_canvas.width/2),-(imageY+img_canvas.height/2));
main_ctx.drawImage(img_canvas, imageX, imageY);
main_ctx.setTransform(1,0,0,1,0,0);
};
But when I call them like this:
loadImages().done( drawToMain );
It makes error as above.
Don't you have any idea how to solve this issue? I need to load my images first a then execute function.

I think you can use multi callback in your function. drawToMain function can be the call back of bg.onload; and bg.onload can be the callback of img_split.onload.
function loadImages(){
var img_split = new Image();
var bg = new Image();
img_split.onload = function(){
var this_canvas = img_canvas_split;
this_canvas.width = w;
this_canvas.height = h;
this_canvas.getContext('2d').drawImage(this, 0,0, w, h);
console.log("img 2");
img_split.src = path;
// First callcack
bg.onload = function(){
var this_canvas = bg_canvas;
this_canvas.width = panorama_w;
this_canvas.height = panorama_h;
this_canvas.getContext('2d').drawImage(this, panoramaX,panoramaY, panorama_w, panorama_h);
console.log("img 3");
bg.src = path_panorama;
drawToMain(); // callback when all is done
};
};
}

The .done() function is used in context of xHR (ajax) callbacks. it will not work on a regular function without a deferred object. I don't see any specific asynchronous requests on this function. I would suggest a callback, as mentioned above, but you could also 'fake' a deferred using $.when(), ie. $.when(loadImages()).done(drawToMain); although this will execute immediately, so not sure what you would gain there.
The best solution would be to add the following to your loadImages() function:
function loadImages(callback){
// rest of code
// right before close tag:
if (!!callback && typeof callback == 'function') {
callback();
}
}
and then when you call the function:
loadImages(drawToMain());
Note, this makes a lot of assumptions about how/where youre loading these scripts and how functions are organized. If you need to access variables in the first function, you could change callback() to callback(this) or callback(bg)

The easiest approach would be to pass in a callback function:
function abc(callback) {
//do some stuff...
callback();
}
abc(function() { console.log('foo'); });

Related

Javascript - canvas - drawImage does not work

I have read all the other similar posts but I don't understand why it I have this problem.
I have a canvas (#canvas) and an image (hero.png) on the page, and a JS file loaded at the end of the body. In the JS code...
This works:
var game = {};
game.canvas = document.getElementById('canvas');
game.ctx = game.canvas.getContext("2d");
game.draw = function(){
game.ctx.drawImage(game.hero, 200, 200);
}
game.hero = new Image();
game.hero.src = "hero.png";
game.hero.onload = game.draw;
And this doesn't work:
var game = {};
game.canvas = document.getElementById('canvas');
game.ctx = game.canvas.getContext("2d");
game.hero = new Image();
game.hero.src = "hero.png";
game.hero.onload = game.draw;
game.draw = function(){
game.ctx.drawImage(game.hero, 200, 200);
}
Nothing appears. No error in the console. Why???
Thanks!
You can call functions before define them only with this syntax:
function draw()
{
//COde here
}
By writting
game.draw = function(){};
You define a method to your object, you don't define a JavaScript function: that's why you can't call it before define it :)
In your second hunk of code, you're defining
game.hero.onload = game.draw;
When game.draw is undefined, so your onload is undefined and nothing happens when the image is done loading.
game.draw is not set when you assign it in your second example, you are copying an undefined value into hero.onload.
One workaround is to wrap the function you want to call in an anonymous function and call yours from there:
game.hero.onload = function(){ game.draw(); };
Have in mind that if hero loads before the definition of game.draw is finished, this code will fail.

JavaScript OOP error

Hello this is my first attempt at trying to write a JavaScript application so I'm new to writing OOP code using it.
The following code runs without any errors in the console:
// Main file for the application
$(document).ready( function()
{
var app = new application;
setInterval( app.run, 50 );
});
function application()
{
var canvas = Raphael(10,0,400,400);
this.molecule = new molecule( new Vec2(50,50),new Vec2(1,0),canvas );
this.molecule.update(10);
this.run = function()
{
}
}
However, this piece of code does not work:
// Main file for the application
$(document).ready( function()
{
var app = new application;
setInterval( app.run, 50 );
});
function application()
{
var canvas = Raphael(10,0,400,400);
this.molecule = new molecule( new Vec2(50,50),new Vec2(1,0),canvas );
this.run = function()
{
this.molecule.update(10);
}
}
It gives the following error in the console:
Uncaught TypeError: Object function molecule( pos,vel,canvas )
{
this.radius = 5;
this.color = "red";
this.canvas = canvas;
this.pos = pos;
this.vel = vel;
this.circle = canvas.circle( this.pos.x,this.pos.y,this.radius );
this.circle.attr("fill", this.color );
} has no method 'update'
Here is the source file containing the molecule object.
// This 'class' handles a molecule, including movement and drawing.
function molecule( pos,vel,canvas )
{
this.radius = 5;
this.color = "red";
this.canvas = canvas;
this.pos = pos;
this.vel = vel;
this.circle = canvas.circle( this.pos.x,this.pos.y,this.radius );
this.circle.attr("fill", this.color );
}
// Updates the molecule
molecule.prototype.update = function( deltaTime )
{
this.pos += this.vel * deltaTime;
this.setPosition(this.pos);
}
// Accepts a Vec2
molecule.prototype.setPosition = function( pos )
{
this.circle.translate( pos.x-this.pos.x, pos.y-this.pos.y );
}
I'm sorry for the large amount of code I've posted, but I'm stumped why the first piece of code works while the second won't. Could anybody shed some light on it for me? Thanks a lot.
A common mistake, and it requires a good understanding of JavaScript to see what's happening here. The problem is this line:
setInterval( app.run, 50 );
This causes app.run to be called when the interval runs out without a proper this context. To ensure that run gets called with app as its this context, you need something like:
setInterval( function() {
app.run();
}, 50 );
or with the latest JavaScript (only in very modern browsers):
setInterval( app.run.bind(app), 50 );
The this context of a function in JavaScript is determined by how the function is called. Basically, it gets determined by what object it is called on. For example, in app.run(), the run method is called on app and it'll work as expected. However, in a slightly different scenario
var fn = app.run;
fn();
the function is called on no object and thus this will not be set, leading to unexpected results. This is exactly what's happening in your case. The solution is to make sure that you pass a function which can be called on any object, and make that function call run on the right object.
You've detached the run method from the app. Pass a function that keeps them together.
setTimeout(function() { app.run(); }, 50);
Now the value of this in .run() will be the app object.
Also, there's no need to make a new run method for every application() object. You can put run on the application.prototype.
function application() {
var canvas = Raphael(10,0,400,400);
this.molecule = new molecule( new Vec2(50,50),new Vec2(1,0),canvas );
this.molecule.update(10);
}
application.prototype.run = function() {
this.molecule.update(10);
}
Although if you did keep run in the constructor, you could then have it close over a variable that references the object, and so you could safely detach it.
function application() {
var canvas = Raphael(10,0,400,400);
this.molecule = new molecule( new Vec2(50,50),new Vec2(1,0),canvas );
this.molecule.update(10);
var self = this;
this.run = function() {
self.molecule.update(10);
}
}
setTimeout(app.run, 50)

jquery.each - passing the counter to child .onload function

somehow my counter variable is not passed to the child function. i'm guessing it's because of some asyncronous behavior but actually i have no clue.
please help.
$(document).ready(function() {
var imgArray = new Array();
$("canvas").each(function(i) {
imgArray[i] = new Image();
});
$.each(imgArray, function(i) {
alert(i);
//correct output
this.onload = function() {
alert(i);
//"undefined"
var width = this.width,
height = this.height;
var context = $("canvas")[i].getContext("2d");
//here's the error
/* more code */
};
this.src = "PATH";
});
});
so how can i pass the value for the right canvas?
thanks for any help!
The problem you're experiencing is due to the nature of how JavaScript supports closures. That is not to say that the problem is a bug; it's behaving exactly as it's should. The onload method is being executed after i has already been iterated all the way through and becomes undefined. A viable solution is to use closures to your advantage and wrap the function creation in a call, such as this.onload = (function(index) { /* ... */ })(i);
This guarantees that the value is stored as expected in an variable internally accessible to the onload methods you're creating.
correct this part of yours to this part.
this.onload = (function(a) {
alert(a);
//"undefined"
var width = this.width,
height = this.height;
var context = $("canvas")[a].getContext("2d");
//here's the error
/* more code */
})(i);
I created a closure so that the i value will be catched as it was on the loop iteration.
I will supply a simple example :
var i=0;
i++;
alert(i) //1
i++;
alert(i) //2
alert(i) //2 !!!
this is what actually happens in you code.

HTML5,Javascript canvas tag image.onload() issue

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

Anonymous function losing scope

Why is this not working?
var img = new Image();
this.dimensions = [ ];
var m = this;
img.onload = function(){
return function(){
m.dimensions = [ this.width, this.height ];
}
};
img.src = 'images/whatever.jpg';
console.log(this.dimensions);
[]
(inside of a JS object, hence the "this")
EDIT: working code
var img = new Image();
this.dimensions = [ ];
var m = this;
img.onload = function(){
m.dimensions.push(this.width, this.height);
console.log(m.dimensions) // had to wait
};
img.src = 'whatever.jpg';
Are you sure console.log(this.dimensions) is being run AFTER the image has loaded? What you've added to img.onload is a callback function to run after the image has finished loading in DOM. Until that img has finished loading, m.dimensions will not be set yet. The image does not load immediately when you add the callback, but loads asynchronously and can finish whenever.
In other words, you are running console.log(this.dimensions) before the dimensions has been set yet (before the onload callback is run). I bet if you wrapped the console.log inside a setTimeout call with say 5 seconds, then it would log what you expected.
That's because this changes meaning inside new function definitions. You solve this by caching this in a local variable. Here, that's already done; your saving it in the variable m. So just use m instead of this.
var img = new Image();
this.dimensions = [ ];
var m = this;
img.onload = function(){
return function(){
m.dimensions = [ m.width, m.height ];
}
};
img.src = 'images/whatever.jpg';
Here's a good tutorial explaining how this changes meaning in different contexts: http://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/
Consider that this code won't alert:
img.onload = function(){
return function(){
alert("hi")
}
};
Then consider the standard (likely desired) "double-closure":
img.onload = (function(m){
return function(){
alert(m)
}
})(this)
Happy coding.

Categories

Resources