I know that javascript doesn't have pointers in terms of a variable referring to a place in memory but what I have is a number of variables which are subject to change and dependent on each other.
For example:
Center (x,y) = (offsetLeft + width/scale , offsetTop + height/scale)
As of now I have rewritten the equation in terms of each individual variable and after any changes I call the appropriate update function.
For example:
If scale changes, then then the center, height, and width stay the same. So I call
updateoffset() {
offsetLeft = centerx - width/scale;
offsetTop = centery - height/scale;
}
Is this the easiest way to update each of these variables when any of them changes?
I read your question in two different ways, so two answers:
Updating calculated values when other values change
The two usual ways are: 1. To require that the values only be changed via "setter" functions, and then you use that as an opportunity to recalcuate the things that changed, or 2. To require that you use "getter" functions to get the calculated value, which you calculate on the fly (or if that's expensive, you retrieve from a cached calculation).
Returning multiple values from a function
If you're looking for a way of returning multiple values from a single function, you can do that easily by returning an object. Example:
// Definition:
function center(offsetLeft, offsetTop, width, height, scale) {
return {
x: offsetLeft + width/scale,
y: offsetTop + height/scale
};
}
// Use:
var pos = center(100, 120, 10, 20, 2);
// pos.x is now 105
// pos.y is now 130
Related
Is there any way to set multiple attributes from the same function?
d3.selectAll('.myshape')
.attr('y',function(d,i) { ... calculates something ... })
.attr('height',function(d,i) { ... calculates something very similar... })
I would like to calculate y1 and y2 at the same time and then set y = y1 and height = y2-y1. But the standard way of doing this in d3 seems to be having separate functions per attribute. Is there a better way?
If I understand your question correctly, you have an intensive calculation that you would like to compute only once per element, regardless the number of attributes you're setting.
That being the case, you can use an each to pass the current element (this), along with the datum, the index and the group, if you need them:
d3.selectAll(".myShape").each(function(d,i,n){
//Here you put the complex calculation
//which will assign the values of y1 and y2.
//This calculation runs only once per element
d3.select(this).attr("y", y1).attr("height", y2 - y1)
});
Not exactly. But you could solve the issue in a indirect way. This adds slightly more overhead but worth it imo. JS doesn't have nice "compile-time" (as far as the js engine is concerned) macros like C family, that give you nice expansions without runtime cost.
like this (you may already know):
let myshape = d3.selectAll('.myshape')
['y', 'height', ...].forEach(attr => myshape.attr(attr, calcAttr));
function calcAttr(a, i) {
// shared code
switch (a) {
case 'y':
//attr specific
break;
}
}
I'd like to create Ampersand State representation of a vector which simultaneously holds information about it's polar and rectangular representation.
i.e. I would like the user to be able to do:
vector.angle = 90
vector.mag = 1
console.log vector.y #=> 1
-or-
vector.x = 0
vector.y = 1
console.log vector.angle #=> 90
Can anyone think of a way to do this with ampersand?
This is the old question, but someone might need this.
Right away I can think of one way to do this. You'd need to make all of your variables independent and then listen to changes to update other values. So you'd define in props of the model variables angle, mag, x, y, and then attach event listeners in initialize of your view or somewhere else to each of these variables. For example, for angle you'd do something like this:
model.on('change:angle', function(model) {
//first, calculate new x and y values. In your model you have a new angle value
if (NEW_CALCULATED_X_VALUE != model.x || NEW_CALCULATED_Y_VALUE != model.y) {
model.set({
x: NEW_CALCULATED_X_VALUE
}, {
silent: true//I'm not triggering the 'change' event yet to avoid circular dependencies.
});
model.set({
y: NEW_CALCULATED_Y_VALUE
}, {
silent: true
});
//now, once I've set all new values, I can trigger events.
model.trigger('change:x', model); //this will trigger model.on('change:x'), but since based on this change angle and mag won't change, no circular dependency will appear.
model.trigger('change:y', model);
}
})
and repeat this for each of four variables (there is room for optimisation, but you get my idea. In order to avoid circular dependencies with this example you need to make sure that once you recalculate, for example, angle by using whatever x, you get the same angle.
I'm trying to get an image to flow horizontally in a sinusoidal fashion, and repeat seamlessly when it gets to the end of its own width in relation to its canvas size.
So far, I've got the image repeating and waving, but there is a significant jump when the x axis needs to be reset.
I think the problem is here:
if (x > canvasX) {
console.log('reset!!');
x = canvasX-imgW;
y = sineY(x);
}
//draw aditional image
if (x > (canvasX-imgW)) {
var ax = x-imgW+dx;
y = sineY(ax);
ctx.drawImage(img,ax,y,imgW,imgH);
}
Ultimately, what happens is that the sineY of the reset x value is about 19 degrees off of what it should be at the end of its regular period where the x value is highest. However, I can't really figure out how to adjust the bounds to make the movement seamless through the multiple periods.
Here's the fiddle: http://jsfiddle.net/3L7Dp/
The period variable needs to be normalized based on the total distance x will move.
In this case x will go image.width so period must be:
var period = x / imgW; //period must be a value in the range [0.0, 1.0]
This should give an usable value for cycling the image.
Modified fiddle
Hope this helps!
One way is to declare an offset by which x will be adjusted, such as var xOffset = 0. When calculating the sine, use x + xOffset. Every time you do x -= imgW, update the offset based on the current offset and the image width, so that the sin at the new position will equal the sin at the current position.
Doing this will allow you to have any period, even one unrelated to the width of your image.
I made my own version of your page with many simplifications, you can see it in this JsFiddle. The sine wave is seamless. My implementation also supports images much narrower than the canvas--they will be repeated all the way across, always filling the canvas (try img.width = 100 in my JsFiddle to see what I mean). In my function, since I based the period on a certain number of x-pixels, my xOffset recalculation is simplified and I can simply use modulus to calculate the new offset after subtracting from x.
Some style considerations I would like to suggest are:
Use more consistent variable names (such as context vs. ctx--if both are truly needed, give them prefixes such as baseContext, canvasContext so that context is consistent throughout the code).
Name variables closer to what they represent (for example, canvasX is not a good variable name for canvas.Width.
Don't be afraid of slightly longer variable names. imgW is less clear than imageWidth. W doesn't always mean width.
Put spaces after commas and the word function, and around operators.
Using parameter x in your sineY function is confusing as x is already declared outside.
Parameterizing your animation function is fine, but just as good is to wrap the entire script in a SEAF (self-executing anonymous function), as that properly gives all the variables a scope (keeping them out of global scope), thus simplifying your code by not having to pass around the variables.
So I have an array (of length 1 for the moment) in Javascript. It contains an Image object for the moment. Basically, I made an animation that works perfectly with this code :
ants[0]._x = 5;
ants[0]._y = 5;
and then, in my updating function :
function animate() {
context.drawImage(ants[0], 0, 0, 158, 160, ants[0]._x, ants[0]._y, 158, 160);
ants[0]._x += 5;
ants[0]._y += 5;
}
The problem is, when I change _x and _y to x and y (like so :
ants[0].x = 5;
ants[0].y = 5;
and everywhere else in the code)
The animation won't work. Moreover, x and y equal to 0 even if I initialized them to 5.
So my question is, is it because my images are Images objects and to add new attributes to a built-in object, you have to add underscores ?
An Image object already has it's own readonly x and y properties. These correspond to the image width and height. Edit: actually corresponds to the position on the page If you're trying to set arbitrary values in your image, you need to create new variables. Previously you were doing this with the underscore character (_x), but you can do it with other characters too
For example:
ants[0].myProperty = 'stackoverflow';
console.log(ants[0].myProperty); // will print 'stackoverflow
You can view all the properties contained in an object with
var ants = new Image;
for (var p in ants) {
console.log(p);
}
MDN has more information on the Image element
There is nothing stopping you from assigning x and y under regular circumstances (ie: if you're using home-made objects, and not built-in language/browser objects).
When you start playing with reserved properties of protected objects, there are all kinds of weird things that can happen, from a browser letting you break the page completely until you refresh, or a browser letting you try for hours to change the definition of window.
It all comes down to how you assign them, how you use them after, whether you're swapping objects out of your array...
...and it's an Image object, so you need to make sure that the image is actually loaded before you can do much with it.
There's really nothing stopping you from doing things like:
var my_character = {
x : 0,
y : 0,
width : 32,
height : 64,
sprite_sheet : loadedImage,
current_frame : 6,
update : function () {
my_character.current_frame += 1;
my_character.x += 3;
my_character.y -= 2;
}
};
context.drawImage(
my_character.sprite_sheet,
x - my_character.width/2,
y - my_character.height/2,
my_character.width,
my_character.height,
my_character.width * current_frame,
0,
my_character.width,
my_character.height
);
That's not a particularly elegant way of doing it, but seriously, if you wanted to then add a my_character.$ = "35.99";, you could.
It's something more than "x" and "y".
If you wanted to use something like my_character.° = 32.5; I believe you'd have to use my_character["°"] = 32.5;
Yes, there's a convention, called Custom Data Attributes. Attributes that begin with data- are reserved for the application, they're guaranteed never to affect the semantics of the elements in the browser.
ant[0].setAttribute("data-x", 5);
ant[0].setAttribute("data-y", 5);
See the official W3C documentation and this blog post by John Resig summarizing it.
I'm trying to make a game in JavaScript using OOP and html5 canvas.
I can not move on with the game beacuse I'm stuck in one place.
My question is how can I change the value of method xPosition that is returning a value from a function.
Below you find some code.
var myObject = {
//placing object in start position
xPosition : function(){
return Canvas.width / 2;
},
//if keycode pressed move myObject to the left
move : function(){
xPosition -= 10; //I know this wont work
}
};
You can't change the return values without changing (ie. overwriting) the xPosition method itself.
However, you can easily get a value and then change that:
… move: function(){
var xval = this.xPosition();
xval -= 10; // this works, reassigning to the "xval" variable (xval=xval-10)
// or in one step:
var xval = this.xPosition() - 10;
}
Or you change the input value of the function. Let it compute the current position from canvas size and a relative position stored in a variable (or easier: in a property) and then change only that variable/property from the move function:
var myObject = {
// placing object in start position
xPos: 0,
getXPosition: function() { // compute value from canvas and position
return Canvas.width / 2 + this.xPos; // or something similar
},
move : function() {
// change relative position
this.xPos -= 10;
}
};
You need to reference the correct scope of the object. The this keyword will reference the myObject scope.
You are also calling a function so need to add the brackets.
Your other issue is that you are calling a function and trying to update the value. You either need to assign xPosition as a variable or pass in the new value.
move : function(){
this.xPosition(-10);
}
Have a look at these articles in structuring javascript
Part 1
Part 2
Part 3
Part 4
If you are dealing with x,y positions and as you say you are making a game, I would use vectors to manage your game objects. The vecotr library will handle all the maths for you. Adding, subtracting, multiplying etc.
There are a lot of vector libraries out there or you can build your own.
Vector Maths