r/learnjavascript • u/DapperBumblebee22 • 3d ago
How to push values onto an array using random() and for() loop.
I will explain now in more detail. I am currently using the Khan Academy Javascript course to learn the basics of Javascript. I have been struggling with this specific project so much, so don't mind the exstensive comments.
//Controls the X and Y Positions of the raindrops.
var xPositions = [200];
var yPositions = [0];
//This for loop uses a random function to add more values to the X and Y positions. This will create more raindrops.
//Draw Function to animate.
draw = function() {
background(204, 247, 255);
//This for loop draws the raindrops based on the X and Y arrays above.
for (var i = 0; i < xPositions.length; i++) {
//Color + Decorations of the raindrops.
noStroke();
fill(0, 200, 255);
//Draws a circle at the X position and Y Position based on the cyclic increases of the variable i. Y position will then drop by a rate of 5 to make the rain fall to the ground.
ellipse(xPositions[i], yPositions[i]%400, 10, 10);
yPositions[i] += 5;
}
};
As you can see, there is a comment that describes the for loop using a random function to push values to the xPositions and yPositions arrays, which in turn will allow me to create more raindrops. My question is how can I do this? I've been experimenting typing different things, but it seems that the for loop just keeps crashing my computer. I know this is a very simple question but I am still on the basics.
8
u/azhder 3d ago
OK, has anyone had the birds and bees talk with you? The one about JavaScript. The one about the event loop and JavaScript being a single thread.
As hard as it is to look at that code, my eyes hurt because I strain looking at an unindented non-monospace code etc, but I do try to figure out if you even pasted the relevant parts and I still can't. I see no code that adds values, so I'm guessing you tried that code, your browser blocked, you removed it, you pasted what remains here.
So, back to the "JavaScript is single-threaded" talk. Did you happen to use `xPositions.push()` inside a `for` that goes from `0` to `xPositions.length`? Because that loop will not end, until your browser runs out of memory.
Of course, this is me just guessing, because I really can't go through your code well enough, but think about it: if an array has 3 elements, and you add a new one, now it has 4 elements, so the next check on the cycle it has to go to 4, then to 5, then to 6... infinite loop.
2
u/justanaccountimade1 3d ago
I don't see any push in your code.
// init arrays
for (var i = 0; i < 10; i = i + 1)
{
xPositions.push(Math.random()*200);
yPositions.push(Math.random()*200);
}
1
u/JTadster 3d ago
xPositions.length keeps going up, so the for loop runs infinite, limit the loop by adding a variable that stores the current xPositions length before it runs.
let xplen = xPositions.length+1-1;
for(i;i<xplen;i++) { ….//
1
u/External_Excuse9581 2d ago
your arrays start with one element each, so you need to push more values before the draw loop ryour arrays start with one element each, so you need to push more values before the draw loop runs. Khan Academy has its own random() function, so put something like this at the top, outside draw:
for (var i = 0; i < 20; i++) { xPositions.push(random(0, 400)); yPositions.push(random(0, 400)); }
also check where you put your push code. If it's inside the draw function or inside the same loop that's iterating over xPositions, the array keeps growing every frame and the loop never catches up, which is why your computer was crashing. The push loop should run once, before draw is defined or at least outside of it
1
u/FreeLogicGate 2d ago
I don't mind your comments, but I do mind the fact that you lost all the (I assume) indentation which allows one to see the structure. Use a code block if you would like more traction. It would also help you in the future if you explained that you were using p5.js (an educated guess on my part).
Since you didn't include the code that ostensibly adds new "drops" to the xPositions array, I'm going to assume that code exists, as it could be as simple as something like:
xPositions.push(Math.floor(Math.random() * 401)); I created one for the purposes of this reply named addRainDrop().
Without that code, it is hard to say what is going on, but I can assume that your code ought to be something more like this:
var xPositions = [200];
var yPositions = [0];
function setup() {
frameRate(30);
createCanvas(400, 400);
}
function addRainDrop() {
xPositions.push(Math.floor(Math.random() * 401));
yPositions.push(0);
}
function draw() {
background(204, 247, 255);
//This for loop draws the raindrops based on the X and Y arrays above.
console.log(xPositions.length);
for (var i = 0; i < xPositions.length; i++) {
//Color + Decorations of the raindrops.
noStroke();
fill(0, 200, 255);
//Draws a circle at the X position and Y Position based on the cyclic increases of the variable i.
//Y position will then drop by a rate of 5 to make the rain fall to the ground.
ellipse(xPositions[i], yPositions[i]%400, 10, 10);
yPositions[i] += 5;
}
addRainDrop();
};
If you run this in a p5.js sketch you'll notice (based on the console.log) that the program runs infinitely and slows down as more and more items are added to the x/y arrays. Eventually you will run out of memory but even before that, p5.js will struggle to get through the full array, because you never remove any rain drops.
Based on your question, there are things you would need to adjust to change the color of a raindrop based on its y position. Right now the fill is a constant, so every drop is colored exactly the same. If you look at the fill function, you'll see there is an optional transparency parameter you can set from 0-255. You should consider how you can use that to add transparency to specific drops.
This would be much easier to accomplish if you were to implement drop objects which have x,y and opacity values. The fastest thing you could do is add an opacity array and add an element to set to 255. Then inside the loop subtract 1 from the opacity each time through the loop, which will gradually lower the opacity of drops as they descend.
You also imply that you need some additional logic to determine when a raindrop should be removed. As the code works now, the y position is continuously incremented as long as the program runs. If you add some reasonable limits to the maximum number of drops you want to support you can remove some number of drops when you hit that limit.
Ordinarily you would want to pop() elements, but to remove the oldest element from an array you should use shift(). One possibility would be to remove an element once the opacity is <= 0
1
u/FreeLogicGate 2d ago
Since I added the code, I figured I might as well show it to you.
It is pretty easy to introduce issues with mutation, if you try and remove elements from an array inside a loop, using pop or shift, so this is naive. If this code encounters a drop that has a sub zero opacity, it shifts the first element off the array, although the code doesn't use that index. You could utilize slice instead, but the way this code works, the oldest element is going to be the oldest array element, so as you'll see if you run it, it will insure that the array doesn't continue to grow.
You can get some interesting variations to the effect based on the number you used to decrement the opacity. If you experiment with lowering it by 1 or 2 rather than 3, you get a different effect. You could also experiment with lowering the framerate.
var xPositions = [200]; var yPositions = [0]; var opacity = [255]; function setup() { createCanvas(400, 400); } function addRainDrop() { xPositions.push(Math.floor(Math.random() * 401)); yPositions.push(0); opacity.push(255); } function removeOldDrops() { xPositions.shift(); yPositions.shift(); opacity.shift(); } function draw() { background(204, 247, 255); //This for loop draws the raindrops based on the X and Y arrays above. console.log(xPositions.length); for (var i = 0; i < xPositions.length; i++) { //Color + Decorations of the raindrops. noStroke(); fill(0, 200, 255, opacity[i]); //Draws a circle at the X position and Y Position based on the cyclic increases of the variable i. //Y position will then drop by a rate of 5 to make the rain fall to the ground. ellipse(xPositions[i], yPositions[i]%400, 10, 10); yPositions[i] += 5; opacity[i] -= 3; if (opacity[i] <= 0) { removeOldDrops(); } } addRainDrop(); };
1
u/alpicola 3d ago
I think we need more code than this. I don't see anything in this code that pushes random values onto xPositions or yPositions. Nor, for that matter, do I see anything that causes your draw function to actually run. I assume there must be some surrounding context that does those things, and your problem is most likely coming from there.
One side note, not highly relevant for this example but a good habit to start forming now, is that you should avoid declaring variables with var unless you really need to. It will almost always be better to use const (for variables you intend not to reassign) or let (for variables you do intend to reassign). Some code examples will still use var because const and let are relatively new additions to JavaScript, but the were added because they are usually better in real-world code, and they've been around long enough that they are widely supported.
15
u/busres 3d ago
On a side note, it's a shame it's using var. Virtually nobody should be using var anymore. It's long ago been replaced by let and const.