I'm trying to create a dynamic canvas animation using javascript. The intent is to increment the x property of many image objects and then draw them in the x position of the updated x property each time a draw() function runs. I'm not able to successfully increment this x property though - for some reason it always resets to 0.
I defined this global array to contain all the objects I will draw:
window.character = [];
I have this object constructor to create new image objects:
function Character(name, x, y){
//define the image object within the Character
this.imageObject = new Image();
this.imageObject.src = name+'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAW0lEQVR42mL8//8/AzpgZGTcC6KBcs5wMRwK/0MVMsLEmLAoEmXAApiwiKUhaRJCltgLsQVsWwIQ/wTx0fBeRigD7B6Y24i1mj4Kn4KI7Uie2Y7FI8+B2AMgwABjRynfWgpcxQAAAABJRU5ErkJggg==';
window.character.push(this);
window.characterPosition = window.character.indexOf(this);
//set natural width and natural height once the image is loaded
if (this.imageObject.addEventListener){
this.imageObject.addEventListener('load', function(){
window.imgWidth = this.naturalWidth/2;
window.imgHeight = this.naturalHeight/2;
//set natural width and natural height to object
window.character[characterPosition]['imageObject']['w'] = window.character[characterPosition]['imageObject']['w0'] = window.imgWidth;
window.character[characterPosition]['imageObject']['h'] = window.character[characterPosition]['imageObject']['h0'] = window.imgHeight;
//set initial x and y position
console.log(x);
window.character[characterPosition]['imageObject']['x'] = x;
window.character[characterPosition]['imageObject']['y'] = y;
console.log(window.character[characterPosition]['imageObject']['x']);
//set loaded property for the object once loading is done
window.character[characterPosition]['imageObject']['loaded'] = true;
function imageLoaded(element, index, array){
return element['imageObject']['loaded'] == true;
}
//test whether every object in array has the image loaded
if(character.every(imageLoaded)){
$('button#play').show();
};
});
} //end object constructor
Inside that constructor function, there is some weird behavior. I created a new object using:
var sun0 = new Character('data:text/javascript;base64,', 12, 12);
Then here's what I see from the console.log messages inside the object constructor:
console.log(x); //returns 12, as expected
window.character[characterPosition]['imageObject']['x'] = x;
window.character[characterPosition]['imageObject']['y'] = y;
console.log(window.character[characterPosition]['imageObject']['x']); //returns 0. Thought it would be 12
Ultimately, here's how I intend to animate the object across the screen. This draw() function runs on an interval every 10ms.
function draw(){
clear();
//draw characters
drawCharacter(window.character[0]['imageObject'],window.character[0]['imageObject']['x'],window.character[0]['imageObject']['y'],window.character[0]['imageObject']['w'],window.character[0]['imageObject']['h']);
window.character[0]['imageObject']['x'] += 10;
console.log(window.character[0]['imageObject']['x']); //returning 0 every time, not incrementing the way I expected it to.
}
How can I get this 'x' property to increment?
here's the JS Fiddle
Pointy answered your question in the comments, but to expand on this... you're trying to set x on an Image which can't be set. For instance, try this in your browser's console:
var img = new Image();
img.x = 1;
console.log(img.x); // this will be zero
That's effectively what you're doing. Additionally, in your code try changing ['x'] to ['myX'] and then it will work as expected.
Related
The problem is I don't understand why this code works. It works, but I just can't wrap my mind around it. Here's a function that deletes a node from a singly linked list. I feel like it shouldn't work because it's not actually changing any of the elements in the list, I'm just changing the value of a variable I have set equal to something in the list. In other words, when I create a "runner" variable to iterate through the list, why are the changes I make to "runner" actually changing the list itself. Analogously, if I do
var x = 1
var y = x
y = 2
Obviously, x is still going to equal 1. Why isn't the same true for my Linked List "runner". In the deleteNode function below, why does changing the runner.next value actually change anything in the node or list that exists outside of the function?
function deleteNode(head, position) {
var runner = head
var counter = 0
while (runner) {
if (counter == position - 1) {
runner.next = runner.next.next
return head;
}
runner = runner.next
counter++
}
}
Its because runner is an object, and so the runner variable is a reference to that object.
for example
const x = {a:1}
const y = x;
x.a = 3
console.log(y.a) // this will print 3 also
I have multiple canvas setup in my HTML page. I access them via their respective ID in JS/jQuery.
In this canvas there is a "player"-character (a basic square), that has a constant size and a variable position.
I create the player object like this:
<script>
var player = {
xpos = 50,
ypos = 50
}
</script>
in the same <script></script> I have a function that looks like this:
async animate(cid2){
var c = Object.create(player);
cid = $(cid2)[0];
ctx = cid.getContext("2d");
c.xpos = 50;
c.ypos = 50;
while(c.xpos < 300){
await sleep(1100);
c.xpos = c.xpos + 50;
//draw a rectangle..
}
Once any <canvas id="test"> is clicked, (with a jQuery .click Function), animate is executed with the respective canvas id.
All of this works great, as long as there is one canvas on the page.
If I have say two canvas, the following happens in the console:
canvas1 is clicked!
X-Position: 50
X-Position: 100
canvas2 is clicked!
X-Position: 150
X-Position: 200
Although the second canvas is clicked and a Object.create should create a new object, it doesn't work: It still accesses the old object.
I am almost certain there is an answer to this on SO, but despite my best efforts, I can't find it, because I don't know what is actually going wrong.
Can anyone help me or refer me to a question I could ask?
Thank you in advance.
You could use the (I think ECMAscript 6) spread operator
const obj = { myProp: 'hi' };
const newObj = { ...obj };
newObj.myProp = 'ciao';
console.log(obj); // still { myProp: 'hi' }
Know you should have two separate objects instead of a copy.
I create bodies at mouse click with this pretty standard way in box2dweb:
*stage.onMouseDown = function(){
var fixDef = new box2d.b2FixtureDef;
fixDef.density = 1;
fixDef.friction = 0.5;
fixDef.restitution = 0.7;
var bodyDef = new box2d.b2BodyDef();
bodyDef.type = box2d.b2Body.b2_dynamicBody;
bodyDef.position.x = mouseX /scale;
bodyDef.position.y = mouseY /scale;
fixDef.shape = new box2d.b2CircleShape(Math.random()*100/scale);
world.CreateBody(bodyDef).CreateFixture(fixDef);}
I really don't know how to insert a name or an Id for my created bodies (eventually I can add a var num++ at every creation). Also, I don't know how to get back my body through the id and call the method .DestroyBody to delete it specifically.
I'm at early stages with JavaScript and Objective C so methods and docs made for Actionscript make me nuts..
Thanks in advance.
Question update:
I found a way to get back my already created object, finding the one i want among all of them using this way:
note: myBody is global
myBody['enter'+prodNum] = bodyDef;
bodyDef.userData = prodNum;
myBody['enter'+prodNum].id = bodyDef.userData;
prodNum is a global var which has a "++" at every cycle. With this i can recall my body back using both the body's var name and the bodyDef.userData property.
with the following function, called in my init() that's executed through window.onload, i can, as console.log shows, change what i want of my retrieved body, however no changes are applied to the body in canvas, even if its property in log resulted modified i cannot notice any change on the screen.
function reduceObj(){
var itsMe;
itsMe = myBody.enter10;
var newPosX = itsMe.position.x;
itsMe.active = false;
itsMe.awake = true;
itsMe.linearVelocity.x = 2000;
itsMe.position.x = newPosX+500;
itsMe.fixedRotation=true;
itsMe.allowSleep=true;
console.log(myBody.enter10,itsMe,itsMe.id,'it s me');
}
Cannot get why all this happens.. plus i already set the step() function which should refresh my world every x milliseconds... Help pls
The CreateBody function should return a reference you can keep, to destroy the body later.
var mybody = CreateBody( bodyDef );
mybody.CreateFixture( fixDef );
You can't just set properties in a body to change it, you need to use the appropriate functions:
// later...
mybody.SetActive( false );
mybody.SetAwake( true );
var vel = mybody.GetLinearVelocity();
vel.x = 2000;
mybody.SetLinearVelocity( vel );
var pos = mybody.GetPosition();
pos.x += 500;
mybody.SetPosition( pos );
mybody.SetFixedRotation( true );
mybody.SetSleepingAllowed( true );
Please keep in mind that 500 units is half a kilometer, so that's probably not what you would want to be doing. Use meters for your dimensions, not pixels. A velocity of 2000m/s is around 7200km/h or mach 6 (as reference the fastest aircraft ever built does about mach 8, so this is also most likely not what you want). Take a look at this page for some other common gotchas: http://www.iforce2d.net/b2dtut/gotchas
I'm working on an implementation of tetris to teach myself javascript and I've run into a problem. First, the background: I have a class for each type of tetris block that inherits from a basic block template with methods like moving and returning coordinates. For instance,
function BlockTemplate(w, h, c){
var height = h;
var width = w;
var x = 0;
var y = 0;
var color = c;
this.getHeight = function(){return height;};
this.getWidth = function(){return width;};
this.getX = function(){return x;};
this.getY = function(){return y;};
this.getColor = function(){return color;};
this.moveTo = function(newX, newY){
x = newX;
y = newY;
};
this.translate = function(dx, dy){
x += dx;
y += dy;
};
}
function StraightBlock(){
this.draw = function(){
ctx.fillStyle = this.getColor();
ctx.fillRect(this.getX(), this.getY(), 20, 100);
};
}
StraightBlock.prototype = new BlockTemplate(20, 100, "#00FFE5");
All blocks currently on the screen are stored in an array, blockArr, except for the currently falling block, which is stored in curBlock.
I use a function called createRandomBlock() to create a block and put it in curBlock:
var blockTypeArr = [LBlock, SquareBlock, StraightBlock, SquigBlock];
var createRandomBlock = function(){
var block = blockTypeArr[Math.floor(Math.random()*blockTypeArr.length)];
var randomBlock = new block();
return randomBlock;
};
curBlock = createRandomBlock();
Once it's done falling I put it in the array and create a new block:
blockArr[blockArr.length] = curBlock;
curBlock = createRandomBlock();
If the newly created block hasn't yet been on screen then there's no problem. However, using the moveTo and translate methods of the new instance affect all instances of that class (I added an id property to make sure they were in fact distinct instances).
For example, using the JavaScript console,
>curBlock
SquigBlock
>blockArr
[SquareBlock, SquigBlock, SquigBlock]
As you can see, 3 SquigBlocks have fallen so far (2 in blockArr, 1 presently falling). Yet the only one I see is the one currently falling (curBlock), and checking the parameters, curBlock.getY(), blockArr[1].getY() and blockArr[2].getY() all return the same value. In other words, they're all being drawn in the same location.
How can I change it so that old blocks, no matter what the class, stay at the bottom of the screen, while the newly created block falls from the top without causing all other blocks of that class to move with it?
Thank you!
StraightBlock.prototype = new BlockTemplate(20, 100, "#00FFE5");
Well, that is only one BlockTemplate that is shared amongst with StraightBlock instances. You can see that new StraightBlock().moveTo == new StraightBlock().moveTo, ie two instances have the very same method which does affect the same x variable. Do not use new for creating the prototype, but Correct javascript inheritance:
function StraightBlock(){
BlockTemplate.call(this, 20, 100, "#00FFE5");
this.draw = function(){
ctx.fillStyle = this.getColor();
ctx.fillRect(this.getX(), this.getY(), 20, 100);
};
}
StraightBlock.prototype = Object.create(BlockTemplate.prototype);
StraightBlock.prototype = new BlockTemplate(20, 100, "#00FFE5");
Since this is called once, there is one var x that is closed over by the single getHeight that is shared among all instances of StraightBlock.
You should make two changes:
1 Use instance fields stored on this
function BlockTemplate(w, h, c){
this.height = h;
...
}
BlockTemplate.prototype.getHeight = function(){return this.height;};
...
2 Chain constructors
Have StraightBlock invoke its super-class constructor to add new versions of the methods to each instance created.
function StraightBlock(){
BlockTemplate.apply(this, arguments);
...
}
You could also just do constructor chaining, but that would mean that you are creating a new copy of each method for each instance. This can be expensive if you have a lot of short-lived instances of a "class".
The drawback of storing fields on this is that they can be unintentionally mutated by naive code. JavaScript does not have strong information hiding except via closed-over local variables, so if you want the robustness that you get via private fields in other languages, then your current design via local variables might be best.
I'm trying to make a helicopter game in javascript. Though atm I have one airplane flying from right to left.
How do I make the array, so I get a constant flow of airplanes (a difference of +-200 spaces on x).
Here is the code that I have atm to add airplanes and I simply don't know how to add the array to it to add different airplanes.
var airplane = new Image();
airplane.src = "images/airplane.png";
var posX = 700;
var posY;
var bmpAirplane;
function init() {
loader.onComplete = handleComplete;
}
function handleComplete() {
displayAirplane();
createjs.Ticker.setFPS(24);
createjs.Ticker.addListener(window);
}
function displayAirplane () {
posY = Math.floor((Math.random()*270)+10);
bmpAirplane = new createjs.Bitmap(airplane);
bmpAirplane.x = posX;
bmpAirplane.y = posY;
stage.addChild(bmpAirplane);
stage.update();
}
function tick()
{
bmpAirplane.x -=10;
}
I am assuming "displayAirplane" method is what creates the airplanes. It looks like "bmpAirplane" is holding the reference to your current airplane. Turn this into an array which will be used to hold many airplanes. Then in your tick() method, loop through this array, updating each airplane object inside it by the airplane speed.