I posted this on gamedev.stackexchange but was referred here so I'll try. I've got this simple menu that is a function, with a mainmenu.prototype.Render to draw it to the screen. Inside the mainmenu function i would like to make an array of objects containing the buttons x, y positions and the .src.
This is my current code that works, so no problem with the function itself:
this.Mainmenu = function() {
}
this.Mainmenu.prototype.Render = function() {
imgPause = new Image();
imgPause.src = 'img/pause.png';
c.drawImage(imgPause, canvas.width - 42, 10);
}
var mainmenu = new self.Mainmenu();
What I would like the final result to look like, but can't get to work (I've included the error in a comment):
this.Mainmenu = function() {
this.button = function(src, X, Y) {
this = new Image(); // Gives error "Invalid left-hand side in assignement"
this.src = src;
this.X = X;
this.Y = Y;
}
this.buttons = [pause = new this.button(src, X, Y)];
}
this.Mainmenu.prototype.Render = function() {
for (i = 0; i < this.buttons.length; i++) {
c.drawImage(this.src, this.X, this.Y);
}
}
var mainmenu = new self.Mainmenu();
But it doesn't work, if anyone can identify where my mistake is it would be appreciated, my patience is about to run out.
Well, your mistake is exactly what your js interpreter says it is - the left side of your assignment is invalid. Namely, you cannot assign this to anything, that's a rule of thumb in all languages that have the this word. The reasoning behind that is obvious - this denotes the current context of the function, the hidden argument of its. If you could overwrite it dynamically, you could alter the behaviour of every single function that is using yours thus the whole program.
How not to use this in this broken way:
this.MainMenu = function() {
this.Button = function(src, X, Y) {
var image = new Image();
image.src = src;
image.X = X;
image.Y = Y;
return image;
}
this.buttons = [pause = new this.Button(src, X, Y)];
}
Also, name your classes with PascalCase (Button, not button) and your variables with camelCase EVERYWHERE (x, not X).
You cannot do this
this.button = function(src, X, Y) {
this = new Image(); // Gives error "Invalid left-hand side in assignement"
}
this represents the current instance of Mainmenu. You cannot override an instance by another
instance.
No sense.
Related
I'm building a game similar to the Chrome dinosaur in Vanilla JS. To animate the obstacles I have created a class Obstacle, which stores their position and size, and defines a method that changes the position.
var Obstacle = function (type, w, h, sprite) {
this.h = h; // Obstacle height
this.w = w; // Obstacle width
this.x = 600; // Starting horizontal position
this.y = GROUND - this.h; // Starting vertical position
this.type = type;
this.sprite = sprite;
this.speed = -4;
this.move = function () {
this.x += this.speed;
}
}
These are stored inside an array, defined as a property of a different class:
var ObstacleBuffer = function () {
this.bufferFront = [];
this.createObstacle = function () {
this.bufferFront.push(this.createBox());
}
// Obstacle creators
this.createBox = function () {
if (Math.random() < 0.5) return new Obstacle ("box1", OBSTACLES.box1.w, OBSTACLES.box1.h, OBSTACLES.box1.sprite);
return new Obstacle ("box2", OBSTACLES.box2.w, OBSTACLES.box2.h, OBSTACLES.box2.sprite);
}
//Obstacle animation
this.animateObstacle = function () {
this.bufferFront[0].move();
}
}
When running this an error pops up:
TypeError: Cannot read property 'move' of undefined.
I have logger the content of this.bufferFront[0] and it correctly show the Obstacle stored inside it.
I have also tried assigning this.bufferFront[0] locally to a variable and then tried to call the method from there. The data stored is correct but the error pops up whenever trying to access Obstacles methods or properties.
Any ideas ? Thank you very much.
EDIT - I have reviewed the code as per your suggestions and the problem seems to be at the point where I'm calling the function. Before generating the obstacles I am preloading a series of images and only generating obstacles once these have load:
this.loadWhenReady = function () {
if (self.resources.isLoadComplete()) {
self.sound.load(self.resources.list.sfx);
drawGround();
this.obstacles.createObstacle(); // <--
self.startGame();
return;
} else {
setTimeout(self.loadWhenReady, 300);
}
}
And this is called in:
setTimeout(self.loadWhenReady, 300);
Of course const self = this has been defined before.
Everything seems to move forward when the method is called outside the SetTimeout.
Why is this happening though ? And is there a way of solving this while calling the method in there ?
SOLVED - As #Bergi and #Jaime-Blandon mention it was a context problem. Calling the method from outside the setTimeout loop or using self.obstacle.createObstacle() instead of this.obstacle.createObstacle() did the trick and solved the issue.
Try this change on the ObstacleBuffer Class:
var ObstacleBuffer = function () {
this.bufferFront = [];
this.createObstacle = function () {
//this.bufferFront.push(this.createBox());
this.createBox();
}
// Obstacle creators
this.createBox = function () {
if (Math.random() < 0.5) this.bufferFront.push(new Obstacle ("box1", OBSTACLES.box1.w, OBSTACLES.box1.h, OBSTACLES.box1.sprite));
this.bufferFront.push(new Obstacle ("box2", OBSTACLES.box2.w, OBSTACLES.box2.h, OBSTACLES.box2.sprite));
}
//Obstacle animation
this.animateObstacle = function () {
this.bufferFront[0].move();
}
}
Best Regards!
When I set the src of an image object, it will trigger an onload function. How can I add parameters to it?
x = 1;
y = 2;
imageObj = new Image();
imageObj.src = ".....";
imageObj.onload = function() {
context.drawImage(imageObj, x, y);
};
x = 3;
y = 4;
In here, I want to use the x and y values that were set at the time I set the src of the image (i.e. 1 and 2). In the code above, by the time the onload function would finish, x and y could be 3 and 4.
Is there a way I can pass values into the onload function, or will it automatically use 1, and 2?
Thanks
All the other answers are some version of "make a closure". OK, that works. I think closures are cool, and languages that support them are cool...
However: there is a much cleaner way to do this, IMO. Simply use the image object to store what you need, and access it in the load handler via "this":
imageObj = new Image();
imageObj.x = 1;
imageObj.y = 2;
imageObj.onload = function() {
context.drawImage(this, this.x, this.y);
};
imageObj.src = ".....";
This is a very general technique, and I use it all the time in many objects in the DOM. (I especially use it when I have, say, four buttons and I want them to all share an "onclick" handler; I have the handler pull a bit of custom data out of the button to do THAT button's particular action.)
One warning: you have to be careful not to use a property of the object that the object class itself has a special meaning or use. (For example: you can't use imageObj.src for any old custom use; you have to leave it for the source URL.) But, in the general case, how are you to know how a given object uses all its properties? Strictly speaking, you can't. So to make this approach as safe as possible:
Wrap up all your custom data in a single object
Assign that object to a property that is unusual/unlikely to be used by the object itself.
In that regard, using "x" and "y" are a little risky as some Javascript implementation in some browser may use those properties when dealing with the Image object. But this is probably safe:
imageObj = new Image();
imageObj.myCustomData = {x: 1, y: 2};
imageObj.onload = function() {
context.drawImage(this, this.myCustomData.x, this.myCustomData.y);
};
imageObj.src = ".....";
Another advantage to this approach: it can save a lot of memory if you are creating a lot of a given object -- because you can now share a single instance of the onload handler. Consider this, using closures:
// closure based solution -- creates 1000 anonymous functions for "onload"
for (var i=0; i<1000; i++) {
var imageObj = new Image();
var x = i*20;
var y = i*10;
imageObj.onload = function() {
context.drawImage(imageObj, x, y);
};
imageObj.src = ".....";
}
Compare to shared-onload function, with your custom data tucked away in the Image object:
// custom data in the object -- creates A SINGLE "onload" function
function myImageOnload () {
context.drawImage(this, this.myCustomData.x, this.myCustomData.y);
}
for (var i=0; i<1000; i++) {
imageObj = new Image();
imageObj.myCustomData = {x: i*20, y: i*10};
imageObj.onload = myImageOnload;
imageObj.src = ".....";
}
Much memory saved and may run a skosh faster since you aren't creating all those anonymous functions. (In this example, the onload function is a one-liner.... but I've had 100-line onload functions, and a 1000 of them would surely be considered spending a lot of memory for no good reason.)
UPDATE: See use of 'data-*' attribute for a standard (and "standards approved") way to do this, in lieu of my ad-hoc suggestion to use myCustomData.
Make a private scope closure that will store x & y values:
imageObj.onload = (function(x,y){
return function() {
context.drawImage(imageObj, x, y);
};
})(x,y);
Make a small function that handles it. Local variables will hold the correct scope.
function loadImage( src, x, y) {
var imageObj = new Image();
imageObj.src = src;
imageObj.onload = function() {
context.drawImage(imageObj, x, y);
};
}
var x = 1,
y = 2;
loadImage("foo.png", x, y);
x = 3;
y = 4;
You could use an anonymous function
x = 1;
y = 2;
(function(xValue, yValue){
imageObj = new Image();
imageObj.src = ".....";
imageObj.onload = function() {
context.drawImage(imageObj, xValue, yValue);
};
})(x,y);
x = 3;
y = 4;
So, I have an <img> tag that has an onclick attribute. The onclick calls a function called analyze(this), with this being the image.
The analyze function does some things to the image that aren't entirely relevant, except for the fact that it draws it onto the <canvas> element (using the drawImage function).
But now, I want to also pick the color I just clicked on in the image. I am currently using the method answered here (the answer with 70+ votes, not the chosen one): How do I get the coordinates of a mouse click on a canvas element?
But, I think I might be doing this wrong. I have the image drawn and my functions called (and those all work), but the color picking part isn't being called. I think that this is because I didn't actually capture the event. This is generally how my code looks:
<img onclick="javascript:analyze(this);" />
function analyze(img_elem) {
// This is getting the canvas from the page and the image in it
var canvaselement = document.getElementById('canvas').getContext('2d'),
img = new Image();
img.onload = function () {
canvaselement.drawImage(img, 0, 0, 250, 250);
...
canvaselement.onClick = function () {
var coords = canvaselement.relMouseCoords(event);
pick(img, canvaselement, coords); // pass in coordinates
}
}
img.src = img_elem.src;
}
function relMouseCoords(event) {
var totalOffsetX = 0;
var totalOffsetY = 0;
var canvasX = 0;
var canvasY = 0;
var currentElement = this;
do {
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
}
while (currentElement = currentElement.offsetParent)
canvasX = event.pageX - totalOffsetX;
canvasY = event.pageY - totalOffsetY;
return {
x: canvasX,
y: canvasY
}
}
function pick(img, canvaselement, coords) {
var pickedColor = "";
canvaselement.drawImage(img, 0, 0, 250, 250);
xx = coords.x;
yy = coords.y;
var imgData = canvas.getImageData(xx, yy, 1, 1).data;
pickedColor = rgbToHex(imgData);
//alert(pickedColor);
return pickedColor;
}
So, the code never gets to the pick function. I have a feeling that it's because I didn't actually capture the onclick event. I'm also not even sure if this is the right way to get the coordinates on the canvas, I'm just sort of hoping that I even get to that part of the debugging process at this point.
Thanks for your help!
The problem is probably that you're assigning canvaselement to the results of getContext('2d') and not to the element itself, which you will need for the click event binding. Create two variables, one for the DOM element itself and one for the context, something like:
var canvaselement = document.getElementById('canvas'),
canvaselementctx = canvaselement.getContext('2d');
...
canvaselement.onClick = function() {
var coords = canvaselementctx.relMouseCoords(event);
...
}
You have a couple of errors in the code but the reason the code you got from the linked post is that you forgot to include the prototype definition it uses:
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
Now you can call relMouseCoords on the canvas element:
/// event name in lower case
canvaselement.onclick = function () {
var coords = canvaselement.relMouseCoords(event);
//...
However, you will still get problems as you don't use a canvas context for the drawing calls.
function analyze(img_elem) {
// This is getting the canvas from the page and the image in it
var canvaselement = document.getElementById('canvas').getContext('2d'),
/// get context like this
ctx = canvaselement.getContext('2d'),
img = new Image();
img.onload = function () {
/// use context to draw
ctx.drawImage(img, 0, 0, 250, 250);
//...
I'm fairly new to object orientated stuff so this may very well be the wrong way to be going about getting this done.
This is a very slimmed down version of what I currently have, but the concept is essentially the same. When the user clicks on a canvas element on my page, I create 20 instances of the particle object below, append them to an array whilst at the same time updating the canvas at 30FPS and drawing circles based on the x property of the instances of each object. Once a particle is off the screen, it's removed from the array.
var particle = function()
{
var _this = this;
this.velocity = 1;
this.x = 0;
this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
var updateObject = function()
{
_this.x += velocity;
}
}
I would like the user to be able to control the velocity of new particles that are created using an input element on the page. When this is updated I have an event listener call
particle.updateVelocity(whateverTheUserEntered);
However I get the error "particle has no method updateVelocity". After a bit of reading up on the subject I understand that to call that function I need to create an instance of the object, but this will only update the velocity value of that instance which isn't going to work for me.
My question is, is there a way to achieve what I'm doing or have I approached this in completely the wrong way? As I said, I'm still getting to grips with OOP principles so I may have just answered my own question...
Try this:
var particle = new (function()
{
var _this = this;
this.velocity = 1;
this.x = 0;
this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
var updateObject = function()
{
_this.x += velocity;
}
})();
Your's is creating a function and then setting the variable particle to that value. particle will not have any special properties because of this. My example above, however, by using new and the function as a constructor assigns particle an instance of a (now anonymous) class.
I think what you want is:
// define a particle "class"
function Particle() {
var _this = {};
_this.velocity = 1;
_this.x = 0;
_this.multiplier = 1;
_this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
_this.updateObject = function()
{
_this.x += velocity;
}
return _this;
}
// make 1 particle
var myParticle = new Particle();
myParticle.updateVelocity(100);
// make a bunch of particles
var myParticles = [];
for (var i=0; i < 100; i++) {
var p = new Particle();
p.updateVelocity(Math.random * 100);
myParticles.push(p);
}
If you change it to
var particle = new function () {
}
The 'new' will cause creation of an instance.
So create a function that builds new particle instances for you.
Make velocity static and have a static method to update it. This way, you can still make instances of particle and update the velocity for all of them.
var particle = function() {
// particle stuff
}
particle.velocity = 1;
particle.updateVelocity = function(newVelocity) {
this.velocity = newVelocity
}
this is my first attempt at oo javascript:
function GuiObject() {
this.x = 0;
this.y = 0;
this.width = 0;
this.height = 0;
this.parent = null;
this.children = [];
this.getWidth = function()
{
return this.width;
};
this.getHeight = function()
{
return this.height;
};
this.paint = function(ctx)
{
};
};
function paintGui(ctx, root)
{
ctx.save();
ctx.translate(root.x, root.y);
root.paint(ctx);
for (int i=0; i<root.children.length; ++i)
{
paintGui(ctx, root.children[i]);
}
ctx.restore();
};
Now in the paintGUI function, the line root.children.lengths throws an error:
Uncaught SyntaxError: Unexpected identifier.
What did i do wrong?
Thanks!
It's hard to say what your actual problem is without looking at the code that actually constructs a GuiObject but for what it's worth, here's a better way to write that 'class'.
function GuiObject() {
this.x = 0;
this.y = 0;
this.width = 0;
this.height = 0;
this.parent = null;
this.children = [];
}
GuiObject.prototype.getWidth = function()
{
return this.width;
};
GuiObject.prototype.getHeight = function()
{
return this.height;
};
GuiObject.prototype.paint = function(ctx)
{
};
Doing it this way, every instance can share the same methods. The other way, you would be creating new function objects for every instance you created. The only reason to ever define the methods in the constructor instead of attaching them to the prototypes is if they need to have access to private members that don't ever get attached to this.
int i? What's that supposed to mean in Javascript then? I think you meant var i.
BTW, In common with all the other people who responded, I looked at your code and didn't spot it immediately. What I then did was copy/paste your function into the Javascript Console and gradually removed lines until it stopped complaining. It's a useful technique to try out little bits of javascript.