I am trying to initialise an array of Image() objects, with each having a different x position. However all the objects in the array seems to end up with the same x position. I have tried searching for a solution and it seems that it is related to 'closure' however I am not sure how to fix it.
This is my code:
function initPieces(){
for (var i = 0; i<8; i++) {
var piece = new Image();
piece.x = 5 + i*50;
piece.y = 5;
piece.src = "images/piece.png";
piecesArray.push(piece);
alert(i + ", " + piecesArray[i].x);
}
}
I have even tried initialising the piecesArray without a loop by manually declaring a new Image() and setting the x position manually for each of the 8 pieces. However it gives me the same problem.
Anyone's help finding a solution will be greatly appreciated.
I couldn't find a definitive reference for the HTMLImageElement.x and HTMLImageElement.y properties (because that's what you are creating with new Image()), but MDN says they are read-only and experimental, not to be used in production code.
And they are indeed read-only: in a quick test I did they could be set, but reading the value afterwards simply produces 0.
If you want to move the elements around in the page after they have been inserted into the DOM, use standard techniques such as manipulating piece.style.top.
You have not yet added those Images to the DOM and so the x, y, offsetLeft, and offsetTop properties have no context (default to zer0).
Either add image to the DOM first before manipulating those, or use a different property:
var piecesArray = [];
function initPieces(){
for (var i = 0; i<8; i++) {
var piece = new Image();
piece.tempX = (i*50 + 5); // temporary place holder for X
piece.y= 5; // this will not work; allows set, but does not change value
piece.src = "images/piece.png";
piecesArray.push(piece);
alert(i + ": " + piecesArray[i].tempX);
}
}
initPieces();
See a demo Fiddle here: http://jsfiddle.net/87xff3fy/1/
A reference for Image.x: http://help.dottoro.com/ljkvjhbq.php (don't use it! Use style or a temporary variable that will set the style.top and style.left at render-time)
Related
Just playing around a bit, but noticed it's taking way too long for page to load, is there anyway to get it to print out one line at a time instead of having to wait till the entire page is loaded.
limits(){
var a = 0;
for (var i = 0; i < 1000; i++) {
for (var ii = 0; ii < 1000; ii++) {
document.getElementById('foo').innerHTML += "<p>" + a + "</p>";
a * 2;
}
}
}
Now how would I be able to control this better to where regardless of how long it takes to load print as soon as ready and even slowing it down would be fine.
The javascript method window.requestAnimationFrame(callback) will call your callback function on the next animation frame. It's commonly used for animation, and will probably work well for what you're doing.
To modify your code to use requestAnimationFrame, you have to make your function print a small chunk on its own, with a reference to know what chunk to print. If you stored your page contents in an array, for example, that could just be a starting index and a length. Since you are printing the increasing powers of 2, you can just pass in the last power of two and the number of lines you want to print for each run of the function.
You'll also need an exit condition -- a check within limits that if true, returns without requesting the next frame. I simply put a hard cap on the value of a, but you could also check that the index is less than array length (for my array of page contents idea above).
Because requestAnimationFrame passes in a function name as a callback, you can't pass arguments into it. Therefore, you have to use bind to sort of attach the values to the function. Then, within the function, you can access them using this. config is just an object to hold the initial arguments you want the function to have, and then you bind it, which allows you to access them within the function with this.numLines and this.a.
Then, when you request the next frame, you have to bind the values to limits again. If you are alright with keeping the arguments the same, you can just do limits.bind(this). But if you want to change them, you can create another object in a similar way to how I wrote config and bind that instead.
The following code seems to be a basic example of roughly what you're looking for:
var foo = document.getElementById('foo');
var maxA = 1000000000000000000000000000000000;
function limits() {
for(var i=0; i<this.numLines; ++i) {
foo.innerHTML += "<p>" + this.a + "</p>";
this.a *= 2;
if(this.a > maxA) {
return;
}
}
requestAnimationFrame(limits.bind(this));
}
config = {
numLines: 3,
a: 1
};
requestAnimationFrame(limits.bind(config));
And is implemented in JSFiddle here. I've also implemented a version where it puts each line at the top of the page (as opposed to appending it to the bottom), so that you can see it happening better (you can find that one here).
You can do something like this:
limits(){
var a = 0;
for (int i = 0; i < 1000; i++) {
for (int ii = 0; ii < 1000; ii++) {
setTimeout(function(){
document.getElementById('foo').innerHTML += "<p>" + a + "</p>";
a * 2;
}, 0);
}
}
}
You can adjust the time in the setTimeout, but even leaving it as zero will allow a more interactive experience even while the page builds. Setting it to 10 or 100 will of course slow it down considerably if you like.
I'm trying to select a random set of three unique elements from an array. I'm newish to JS, and I'm constantly tripping over reference behavior that is unexpected (Python is my best language). I think that's happening here, too. This is P5.JS.
Here's my attempt:
var points = [[0,0],[.5*w,0],[w,0],
[0,.5*h],[.5*w,.5*h],[w,.5*h],
[0,h],[.5*w,h],[w,h]];
var vert = [];
var start = int(random(0,8));
vert.push(points[start].slice());
points.splice(start,1);
var middle = int(random(0,7));
vert.push(points[middle].slice());
points.splice(middle,1);
var end = int(random(0,6));
vert.push(points[end].slice());
When I look at the contents of vert, it's clear that I'm not getting the elements that I expected. In particular, I'm never getting any of the last three elements in the original array.
As noted above, the int() and random() are p5.js functions, and fine. The issue was fixed by removing the .slice() instances in the push() statements:
var vert = [];
var start = int(random(0,8));
vert.push(points[start]);
points.splice(start,1);
var middle = int(random(0,7));
vert.push(points[middle]);
points.splice(middle,1);
var end = int(random(0,6));
vert.push(points[end]);
To demonstrate this (I'm working on a much more compressed Fiddle), open this in chrome, and move Jay-Z using your arrow keys and catch about 4 - 5 (sometimes more!) cakes.
You will notice that there is a massive cupcake on the left side of the screen now.
I update the cakes' positions in my handleTick function, and add new cakes on a time interval. Here are both of those:
/*This function must exist after the Stage is initialized so I can keep popping cakes onto the canvas*/
function make_cake(){
var path = queue.getItem("cake").src;
var cake = new createjs.Bitmap(path);
var current_cakeWidth = cake.image.width;
var current_cakeHeight = cake.image.height;
var desired_cakeWidth = 20;
var desired_cakeHeight = 20;
cake.x = 0;
cake.y = Math.floor((Math.random()*(stage.canvas.height-35))+1); //Random number between 1 and 10
cake.scaleX = desired_cakeWidth/current_cakeWidth;
cake.scaleY = desired_cakeHeight/current_cakeHeight;
cake.rotation = 0;
cake_tray.push(cake);
stage.addChild(cake);
}
And the setInterval part:
setInterval(function(){
if (game_on == true){
if (cake_tray.length < 5){
make_cake();
}
}
else{
;
}
},500);
stage.update is also called from handleTick.
Here is the entire JS file
Thanks for looking into this. Note once again that this only happens in Chrome, I have not seen it happen on Firefox. Not concerned with other browsers at this time.
Instead of using the source of your item, it might make more sense to use the actual loaded image. By passing the source, the image may have a 0 width/height at first, resulting in scaling issues.
// This will give you an actual image reference
var path = queue.getResult("cake");
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.
consider this code:
var deSaturated = deSaturate(greyscaleCtx.getImageData(0, 0, canvasWidth, canvasHeight));
imageData comes from getImageData canvas function.
function deSaturate (imageData) {
var theData = imageData.data;
var dataLength = theData.length;
var i = dataLength-1;
var lightLevel;
// Iterate through each pixel, desaturating it
while ( i >= 0) {
// To find the desaturated value, average the brightness of the red, green, and blue values
theData[i] = theData[i+1] = theData[i+2] = (theData[i] + theData[i + 1] + theData[i + 2]) / 3;
// Fully opaque
theData[i+3] = 255;
// returning an average intensity of all pixels. Used for calibrating sensitivity based on room light level.
lightLevel += theData[i]; //combining the light level in the samefunction
i -= 4;
}
imageData.data = theData; //bring back theData into imageData.data - do I really need this?
var r = [lightLevel/dataLength,imageData]
return r;
}
during the writing and optimizing of this code I found out I don't really understand how js is treating for example "theData" variable. is working with it just a short way to reference imageData.data in which case I don't need the following code in the end:
imageData.data = theData
but then do I pay in degraded performance ( a lot of DOM I/O)?
or is doing theData = imageData.data actually copying the original array (represented as Uint8ClampedArray) and then I have to reassign the modified data to imageData.data.
I guess this is basic javascript, but I found contradictory code examples in MDN and other developer resources and I would really like to understand this properly.
thanks for the help!
Just ran a quick test:
var idata = ctx.getImageData(0,0,300,300);
var data = idata.data;
for(var i=0;i<data.length;i++){
data[i]=0;
}
ctx.putImageData(idata,0,0);
And that properly blanks out part of the screen as expected. However without putImageData nothing will happen. So changing the data object, whether stored in a different variable or not, will be reflected in that imageData object. However this will not affect the canvas until putImageData has been called.
So, yes, you can remove that final assignment and it will work as desired.
However I will warn that it is not a valid assumption that it is a Uint8ClampedArray. Yes, that is how Chrome handles it (last I checked), and it is indeed what the official specification uses. However some browsers have no notion of Uint8ClampedArray, while still supporting canvas through the now deprecated CanvasPixelArray.
So all you are guaranteed to get is something with the some array-like interface. I had to learn this the hard way when I tried to cache interesting features of image data by creating a new Uint8ClampedArray, which failed in some browsers.
See: https://developer.mozilla.org/en-US/docs/DOM/CanvasPixelArray
In javascript, assigning either an array or an object just assigns a reference to that array or object - it does not make a copy of the data. A copy is only made if you physically create a new array and copy the data over or call some function that is designed to do that for you.
So, if imageData.data is an array, then assigning it to theData just makes a shortcut for referring to the same data. It does not make a new copy of the data. Thus, after modifying the data pointed to by theData, you don't have to assign it back to imageData.data because there is only one copy of the data and both theData and imageData.data point already point to that same copy of the data.
So, in direct answer to your question, this line is unnecessary:
imageData.data = theData;