Seems simple enough, but I cannot get it to work:
// Demonstrative only, how can I access canvas from rotate?
_imagePreview = function()
{
var canvas = '';
return {
load: function() {
fileReader.onload = function(e) {
image = new Image();
image.onload = function() {
// Empty string (expected)
console.log(this.canvas);
canvas = $('#myCanvas')[0];
// Canvas object (expected)
console.log(this.canvas);
return true;
}
}
image.src = e.target.result;
}
fileReader.readAsDataURL(file);
},
rotate: function() {
// Undefined (unexpected)
console.log(canvas);
}
}
I call _imagePreview.load() first, then _imagePreview.rotate(), but I can't seem to get the correct value for canvas.
The rotate method is being called from a click event, after the selects a file and it is drawn into the canvas, so rotate will never be executed before load. Example:
$("#rotate-clockwise").click(function() {
_imagePreview().rotate('+', 90);
});
$("#rotate-counter-clockwise").click(function() {
_imagePreview().rotate('-', 90);
});
The problem is that you are calling _imagePreview() multiple times. Each call creates a new set of load and rotate functions with their own canvas variable.
Call _imagePreview once, store the result call load and rotate from that object.
You simply create the private variables first, then return an object that has functions that use those private variables.
_imagePreview = function() {
var canvas = 'I am a canvas!';
return {
load: function() {
canvas = 'I am a loaded canvas!';
},
rotate: function() {
console.log(canvas);
}
};
}
_imagePreview().rotate();
// I am a canvas!
var preview = _imagePreview();
preview.load();
preview.rotate()
// I am a loaded canvas!
That Javascript isn't even valid. Are you getting errors (you will be)? The returned object should look something like this (note the extra enclosing curly braces).
return {
load: function() {
// ...
},
rotate: function() {
// ...
}
};
If you want to access rotate from load following the sort of approach you are using you might do this:
var _rotate = function() {
// ... do rotation
};
var _load = function() {
_rotate();
// ... do some other stuff ...
};
return {
load: _load,
rotate: _rotate
};
Related
I'm not sure what's wrong here, but testing in the chromium and firefox, I find that I'm doing it wrong with respect to removing an EventListener from an element in javascript.
The context is a canvas game. At first, there's a splash screen shown where you click to begin the game. After you click to begin, I want to remove the listener.
The main point of interest is the removeEventListener in the startGame function. It doesn't throw an error. And the code executes (I see the game starting message in the console and I can see that "this" is the Game instance). I'm totally confused why if I keep on clicking on the canvas runs startGame each time. The expected behavior is that clicking there does nothing once the EventListener is removed.
Help!
function Game(canvas) {
this.c = canvas;
this.ctx = this.c.getContext("2d");
this.c.width = CANVAS_WIDTH;
this.c.height = CANVAS_HEIGHT;
// Background image
this.bgReady = false;
this.bgImage = new Image();
this.bgImage.onload = function () {
window.g.bgReady = true;
};
this.bgImage.src = MAIN_BACKGROUND;
}
Game.prototype.setSplash = function() {
if (this.bgReady) {
this.ctx.drawImage(window.g.bgImage, 0, 0);
this.ctx.font="48px Helvetica";
this.ctx.textAlign = "center";
this.ctx.fillStyle="rgb(0,0,255)";
this.ctx.fillText("Click To Start",310,240);
document.getElementById("cnvs").addEventListener(
'click',this.startGame.bind(this),true);
} else {
// since setSplash is an early function
// wait a bit for the background image and then try again
setTimeout(this.setSplash.bind(this),100);
console.log("bgImage not ready...");
}
}
Game.prototype.startGame = function() {
console.log("game starting ...");
console.log(this);
// step 1, remove the click listener for this function
// why isn't this working?!
document.getElementById("cnvs").removeEventListener(
'click',this.startGame,true);
}
...
// other stuff ...
function initialize() {
// Get the canvas
var c = document.getElementById("cnvs");
// Create a game object
window.g = new Game(c);
// Set the splash page
g.setSplash();
}
window.onload=initialize;
Further info:
I also had a version where the non-working removal was written as:
this.c.removeEventListener('click',this.startGame,true);
Same behavior as the code referenced above.
EDIT: in reply to the first answer by mczepiel
I'm trying to implement your answer like this:
Typer.prototype.setSplash = function() {
if (this.bgReady) {
this.ctx.drawImage(window.t.bgImage, 0, 0);
this.ctx.font="48px Helvetica";
this.ctx.textAlign = "center";
this.ctx.fillStyle="rgb(0,0,255)";
this.ctx.fillText("Click To Start",310,240);
var boundFunction = this.startGame.bind(this);
document.getElementById("cnvs").addEventListener(
'click',boundFunction,true,boundFunction);
} else {
// since setSplash is an early function
// wait a bit for the background image and then try again
setTimeout(this.setSplash.bind(this),100);
console.log("bgImage not ready...");
}
}
Typer.prototype.startGame = function(boundFunction) {
console.log("game starting ...");
console.log(this); // strangely, now this is an Object rather
// than Game, it still has the properties of
// Game tho
// step 1, remove the click listener for this function
// still isn't working...
document.getElementById("cnvs").removeEventListener(
'click',boundFunction,true);
}
I think I understood your suggestion, but perhaps not. The code above still doesn't remove the listener. Any help appreciated.
You'll need to store a reference to the result of calling this.startGame.bind(this) and pass that same value to both addEventListener and removeEventListener
The remove call is expecting to remove the exact same object that was added as a listener.
Likely duplicate of removeEventListener is not working and others if you want to see the same issue in various flavors.
EDIT untested off-the-cuff suggestion:
Typer.prototype.setSplash = function() {
if (this.bgReady) {
// draw stuff
var canvasElement = document.getElementById("cnvs");
var dismissSplash = function (evt) {
canvasElement.removeEventListener('click', dismissSplash, true);
this.startGame();
}.bind(this);
canvasElement.addEventListener('click', dismissSplash, true);
} else {
// try to show splash later
}
}
Typer.prototype.startGame = function() {
// start game
}
I draw several images with a function that performs something similar to:
context.drawImage(img, width / 2 * (-1), height / 2 * (-1), width, height);
I've read that I need to wait for the image to be loaded before I can draw it, with something like this:
img.onload = function() {
context.drawImage(img, width / 2 * (-1), height / 2 * (-1), width, height);
};
However this causes the image to be drawn but afterwards, nothing is drawn since I call up my draw function every few milliseconds as it's part of the 'animation' loop for a simple game.
Is there a way that I can wait for the onload event before I continue running code in my init() function?
I suppose something like:
var image_loaded = false;
img.onload = function() {
image_loaded = true;
};
if(image_loaded) {
animate();
}
Should work? Unless I need to add a timeOut function to keep calling the init() until image_loaded is true?
Live Demo
var imagesLoaded = [],
images = [
'http://www.zombiegames.net/inc/screenshots/The-Last-Stand-Union-City.png',
'http://www.zombiegames.net/inc/screenshots/Lab-of-the-Dead.png',
'http://www.zombiegames.net/inc/screenshots/Zombotron.png',
'http://www.zombiegames.net/inc/screenshots/Road-of-the-Dead.png'],
canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = 640;
canvas.height = 480;
function init(){
// just loops through the images.
if(imagesLoaded.length > 0){
ctx.drawImage(imagesLoaded[0], 0, 0, 640, 480);
imagesLoaded.push(imagesLoaded.shift());
}
setTimeout(init,500);
}
function preload(){
for(var i = 0; i < images.length; i++){
(function(value){
return function(){
var loadedImage = new Image();
loadedImage.src = images[value];
loadedImage.onload = function(){
imagesLoaded.push(loadedImage);
}
}();
})(i);
}
checkLoaded();
}
function checkLoaded(){
if(imagesLoaded.length === images.length){
console.log(imagesLoaded.length);
init();
} else{
setTimeout(checkLoaded,30);
}
}
preload();
Above is an example of how to preload images and wait to do anything else. Basically what you do is have all your images in an array, and add the onload event to each of them. As they load I through them into another array that holds all the loaded images. Once the length of the two arrays match all of the images are ready to use.
Another way would be to increment a counter as they load and check its value against the length of the array. When the counter variable is equal to the length of the images array it means they've all loaded and are ready to use.
I created a simple and small library to make the load of images easy.
Check the demo
See the library code below:
// Simple Image Loader Library
window.Loader = (function () {
var imageCount = 0;
var loading = false;
var total = 0;
// this object will hold all image references
var images = {};
// user defined callback, called each time an image is loaded (if it is not defined the empty function wil be called)
function onProgressUpdate() {};
// user defined callback, called when all images are loaded (if it is not defined the empty function wil be called)
function onComplete() {};
function onLoadImage(name) {
++imageCount;
console.log(name + " loaded");
// call the user defined callback when an image is loaded
onProgressUpdate();
// check if all images are loaded
if (imageCount == total) {
loading = false;
console.log("Load complete.");
onComplete();
}
};
function onImageError(e) {
console.log("Error on loading the image: " + e.srcElement);
}
function loadImage(name, src) {
try {
images[name] = new Image();
images[name].onload = function () {
onLoadImage(name);
};
images[name].onerror = onImageError;
images[name].src = src;
} catch (e) {
console.log(e.message);
}
}
function getImage(/**String*/ name){
if(images[name]){
return (images[name]);
}
else{
return undefined;
}
}
// pre-load all the images and call the onComplete callback when all images are loaded
// optionaly set the onProgressUpdate callback to be called each time an image is loaded (useful for loading screens)
function preload( /**Array*/ _images, /**Callback*/ _onComplete, /**Callback <optional>*/ _onProgressUpdate) {
if (!loading) {
console.log("Loading...");
loading = true;
try {
total = _images.length;
onProgressUpdate = _onProgressUpdate || (function(){});
onComplete = _onComplete || (function(){});
for (var i = 0; i < _images.length; ++i) {
loadImage(_images[i].name, _images[i].src);
}
} catch (e) {
console.log(e.message);
}
} else {
throw new Error("Acess denied: Cannot call the load function while there are remaining images to load.");
}
}
// percentage of progress
function getProgress() {
return (imageCount / total)*100;
};
// return only the public stuff to create our Loader object
return {
preload: preload,
getProgress: getProgress,
getImage: getImage,
images: images // have access to the array of images might be useful but is not necessary
};
})();
How it works
To make sure that images are loaded and they could be used by your application the library have the Loader.preload method.
The preload method will receive an array of objects, each object containing the name and the src properties of an image you want to load. Optionally you can setup the onComplete callback (to be called when all images are loaded) and the onProgressUpdate callback (to be called each time an image is loaded). The onProgressUpdate callback is useful if you want to create a loading screen for your application.
Use the Loader.getProgress() to obtain the percentage of images loaded at any time.
To obtain the reference of an image loaded, call Loader.getImage(name) where name is the name property (a String) of the image.
If you for some reason needs iterate over all images use Loader.images. It's the object containing all references for the images in its properties.
Use like this:
var images = [{name: "img1", src: "http://...a.."},
{name: "img2", src: "http://...b.."},
...
{name: "imgN", src: "http://...N.."}];
function onProgressUpdate(progress){
...
drawProgressBar(progress); // just an example of what you can do
...
}
function onComplete(){
...
// get an Image from Loader object
var texture = Loader.getImage("img2");
// or use this:
var texture = Loader.images.img2; // or still Loader.images["img2"]
...
// iterate over the images
for (var img in Loader.images){
console.log(Loader.images[img].src);
}
....
}
Loader.preload(images, onComplete, onProgressUpdate);
Check the demo if you didn't.
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)
I have always had trouble working with time events. Could someone please explain why A doesn't work and B does? The only difference is in A I put the event binding in a function. Don't worry about the function close, it has nothing to do with the question. When I test A, there is no js errors but timer is not cleared.
A ->
Test.Navigation = (function() {
var openTimer = null;
var closeTimer = null;
var addListeners = function() {
$('.hover_container').on('mousemove', function(e) {
clearTimeout(closeTimer);
});
$('.hover_container').on('mouseleave', function(e) {
// set the close timer
var container = this;
closeTimer = setTimeout(function() {
//has the mouse paused
close(container);
}, 750);
});
};
return {
init : function() {
addListeners();
}
};
})();
B ->
Test.Navigation = (function() {
var openTimer = null;
var closeTimer = null;
$('.hover_container').on('mousemove', function(e) {
clearTimeout(closeTimer);
});
$('.hover_container').on('mouseleave', function(e) {
// set the close timer
var container = this;
closeTimer = setTimeout(function() {
//has the mouse paused
close(container);
}, 750);
});
var addListeners = function() {
// nothing here
};
return {
init : function() {
addListeners();
}
};
})();
Edit: Please ignore the container part, it has nothing to dow ith the question it is simply part of the full code that I did not take out
A is binded before the object exists where the init is called. Because your return a new object. If you are using, 2 objects are created. 1 with the vars en binds. and 1 with the returns.
B is working because you create a function where the elements are initialized and use the right scope. A is not working because the bindings are on the wrong scope because your create 2 objects:
new Test.Navigation(); // Create 1 object
// Create second object.
return {
init : function() {
addListeners();
}
};
Youd better get a structure like this, then it should work aswell:
Test.Navigation = (function() {
// Private vars. Use underscore to make it easy for yourself so they are private.
var _openTimer = null,
_closeTimer = null;
$('.hover_container').on('mousemove', function(e) {
clearTimeout(_closeTimer );
});
$('.hover_container').on('mouseleave', function(e) {
// set the close timer,
// use $.proxy so you don't need to create a exta var for the container.
_closeTimer = setTimeout(
$.proxy(function() {
//has the mouse paused
close(this);
}, this)
, 750);
});
this.addListeners = function() {
// nothing here
};
this.init = function() {
this.addListeners();
}
// Always call the init?
this.init();
return this; // Return the new object Test.Navigation
})();
And use it like
var nav = new Test.Navigation();
nav.init();
Also as you can see I upgraded your code a bit. Using $.proxy, _ for private vars.
Your use of this is in the wrong scope for the first approach.
Try
var openTimer = null;
var closeTimer = null;
var self = this;
and then later
var container = self;
In your code for example A,
$('.hover_container').on('mouseleave', function(e) {
// set the close timer
var container = this;
this is actually referring to the current $('.hover_container') element.
Also, since setTimeout will wait before the previous setTimeout finishes to start again, you can get discrepancies. You may want to switch to setInterval because it will issue its callback at every interval set regardless of if the previous callback has completed.
My guess is that in the calling code, you have a statement new Test.Navigation() which, for B, addListeners is called at the time of new Test.Navigation(). In A, you return an object ref that calls an init function. Can you verify that init() is called?
I.e. in A, init() has to be called before the handlers are added. In B, the handlers are added everytime you instantiate Test.Navigation --- which, depending on the calling code, could be bad if you intend to instantiate more than one Test.Navigation() at a time.
Currently I'm trying to write a resourcemanager in JavaScript. It has two methods, one to add a image resource to the manager, and one that preloads all the images after they are added:
ResourceManager = function(){};
ResourceManager.prototype = function(){
var imageList = new Array(),
addImage = function(imageUrl){
imageList.push(imageUrl);
},
loadImages = function(){
//Do stuff for loading here
onComplete();
},
onComplete = function(){
alert('finished');
};
return {
addImage: addImage,
loadImages: loadImages,
onComplete: onComplete
}
}();
Then I want to use it as following:
var rsmgr = new ResourceManager();
rsmgr.onComplete = function(){
alert('completed');
};
rsmgr.addImage('image.png');
rsmgr.loadImages();
You can see a working example on jsfiddle
Now this overload is not working, so why does this happen? I'm guessing it has to do with the prototyping but I can't seem to grasp on how to fix this.
I'm no expert in prototyping, so can't give valid response why it's not working however I can suggest alternative way using "pure function" approach that works just fine:
function ResourceManager() {
this.imageList = new Array();
this.addImage = function(imageUrl) {
this.imageList.push(imageUrl);
};
this.loadImages = function() {
//Do stuff for loading here
this.onComplete();
};
this.onComplete = function() {
alert('finished');
};
};
Having it written like the above cause your original override code to work - updated fiddle.
(If you remove the rsmgr.onComplete override you'll get "finished" as expected)