Troubleshooting an infinite loop error - javascript

I am trying to draw an array of images on canvas at random x,y but it gives me an infinite loop.... here is my code
var fruits = ["fruit1.png", "fruit2.png", "fruit3.png", "fruit4.png"];
var monsterReady1 = true;
var draw = function() {
for (var i = 0; i < fruits.length; i++) {
monsterImage1 = new Image();
monsterImage1.onload = function () {
monster1.x = (Math.random() * (canvas.width - 100));
monster1.y = (Math.random() * (canvas.height - 100));
ctx.drawImage(this, monster1.x, monster1.y);
};
monsterImage1.src = fruits[i];
}
};
var render = function() {
if (monsterReady1) {
draw();
}
var main = function () {
update();
render();
requestAnimationFrame(main);
};

You have recursive in main() function. This is normal behaviour of requestAnimationFrame(). It is normal to call that function infinite to draw canvas each frame.
There is also recursion when render() executes. You don't need to call render again and again. Pass out render call from render() body
var render = function(){
if (monsterReady1) {
draw();
}
var main = function () {
update();
render(); // THIS is error. You should not call render again
requestAnimationFrame(main); // This will call main function infinite loop. Expected.
}
};
//render(); // Better to call it here
By the way in code you provide there is a syntax error. You missed one closing bracket

Related

I can't access my javascript code because of infinite prompts [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed last year.
Improve this question
Help, I've been using this website called "codepen" to do some javascript coding. But I accidently made it send me infinite prompts, everytime I open the project. Now I can't access the code. I searched for some time for an answer but I found none. heres a link to the problem: https://codepen.io/Aibel-Roy/pen/zYPBeEW
//I can't post the code because of the infinite prompts. Sorry.
Here's your code:
//config
var tick = 50;
var fieldOfView = 25;
var Speed = 0.25;
var ZMulti = 4;
var ClearOnDraw = true;
// variables
var keymap = [];
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var meshA = [
"0,0,0",
"1,0,0",
"1,1,0",
"0,1,0",
"0,0,1",
"1,0,1",
"0,1,1",
"1,1,1"
];
var textures = [
"http://www.textures4photoshop.com/tex/thumbs/red-sofa-leather-seamless-texture-53.jpg"
];
var cameraData = [0, 0, 0];
//keymap
window.addEventListener(
"keydown",
(event) => {
var name = event.key;
keymap.push(name);
},
false
);
window.addEventListener(
"keyup",
(event) => {
var name = event.key;
if (keymap.includes(name)) {
keymap.splice(keymap.indexOf(name), 1);
}
},
false
);
//render img
function draw() {
if (ClearOnDraw) {
ctx.clearRect(0, 0, 10000, 1000);
}
var img = new Image(); // texturing
img.src = textures[0];
prompt(img.src);
img.onLoad = function () {
var pattern = context.createPattern(imageObj, "repeat");
ctx.fillStyle = pattern;
var prevVert;
for (let i = 0; i <= meshA.length; i++) {
//convert 3D vector to 2D
var vert = meshA[i];
if (i >= meshA.length) {
vert = meshA[0];
}
var vertPos = vert.split(",");
var zMag = (vertPos[2] - cameraData[2]) * (fieldOfView / ZMulti);
var vertPos2D = [
(vertPos[0] - cameraData[0]) * fieldOfView + zMag,
(vertPos[1] - cameraData[1]) * fieldOfView + zMag
];
ctx.beginPath();
ctx.moveTo(vertPos2D[0], vertPos2D[1]);
for (let i1 = 0; i1 < meshA.length; i1++) {
var prv = meshA[i1].split(",");
var PrevzMag = (prv[2] - cameraData[2]) * (fieldOfView / ZMulti);
var I1VertPos = [
(prv[0] - cameraData[0]) * fieldOfView + PrevzMag,
(prv[1] - cameraData[1]) * fieldOfView + PrevzMag
];
ctx.lineTo(I1VertPos[0], I1VertPos[1]);
ctx.stroke();
}
ctx.closePath();
ctx.fill();
prevVert = vertPos2D;
}
};
}
function Movement() {
if (keymap.includes("w")) {
cameraData[2] -= Speed * 2;
}
if (keymap.includes("s")) {
cameraData[2] += Speed * 2;
}
if (keymap.includes("d")) {
cameraData[0] -= Speed;
}
if (keymap.includes("a")) {
cameraData[0] += Speed;
}
}
draw();
setInterval(function main() {
draw();
Movement();
}, tick);
How to disable the prompt(if the browser doesn't suggest you to suppress it):
On that page, bring up dev tools(Command + Option + I, or F12 on Windows).
Choose the correct page on the dev tool, usually looks like CodePen (Hash ID)
Override the prompt function in the console by typing window.prompt = () => {}.
Change your code, save and refresh the page.
There are probably better ways to do it but disabling JavaScript makes the code section unusable.
In your function draw(), there is a prompt in here:
function draw() {
// Trimmed
prompt(img.src);
// Trimmed
}
You also have setInterval calling draw() at a particular interval defined in tick (where you have assigned 50ms as the value):
setInterval(function main() {
draw();
Movement();
}, tick);
As a result, draw() gets called every 50ms, where prompt(img.src) gets called, leading to infinite prompt.
You need to change whatever you are doing in setInterval().

html canvas element blinking and then bugging out

I'm trying to have asteroids moving across the screen for a game. The first few asteroids work and then each asteroid will start blinking and bugging out to the point where they won't move across the screen. The variables acx and acy are the x and y coordinates for the asteroids respectively.
setInterval(throwAsteroid1A, 5000);
function throwAsteroid1A() {
var asteroidCanvas = document.getElementById('asteroidCanvas');
var context = asteroidCanvas.getContext('2d');
var acx = Math.floor(Math.random() * 200);
var acy = Math.floor(Math.random() * 10);
setInterval( () => {
asteroid.onload = function() {
context.drawImage(asteroid, asx, asy, aswidth, asheight, acx, acy, 20, 20);
acx += 1;
acy += 1;
}
asteroid.src = 'https://i.imgur.com/WfQKE6T.png';
}, 10)
setInterval(asteroidPath, 50)
}
function asteroidPath() {
// let computedStyle = getComputedStyle(canvasDisplay)
var asteroidCanvas = document.getElementById('asteroidCanvas');
let ctx = asteroidCanvas.getContext("2d");
ctx.clearRect(acx,acy, canvasDisplay.width, canvasDisplay.height);
}
Well there's obviously something conceptually wrong with your approach. I think the blinking is caused by a timing issue in-between the numerous individual interval timers you set up. The callback function asteroidPath() clears a part of the canvas and this might happen at the same time a new Asteroid has been added to the screen - which will delete it either entirely or partly depending on it's screen position.
To work around it you should:
keep a list of all asteroid objects
clear the screen completely once
update all asteroid's at once - not each one with it's own timer
So an example based on your code might look a little something like this (just click 'Run code snippet'):
Asteroid = function() {
this.acx = Math.floor(Math.random() * 200);
this.acy = Math.floor(Math.random() * 10);
this.image = new Image();
this.image.onload = function(e) {
this.loaded = true;
this.aswidth = e.target.naturalWidth;
this.asheight = e.target.naturalHeight;
}
this.image.src = 'https://i.imgur.com/WfQKE6T.png';
}
var asteroidCanvas = document.getElementById('asteroidCanvas');
var context = asteroidCanvas.getContext('2d');
let asteroids = [];
function spawnAsteroid() {
asteroids.push(new Asteroid());
}
function updateCanvas() {
context.clearRect(0, 0, asteroidCanvas.width, asteroidCanvas.height);
let asteroid;
for (let a = 0; a < asteroids.length; a++) {
asteroid = asteroids[a];
if (asteroid.image.loaded) {
context.drawImage(asteroid.image, 0, 0, asteroid.image.aswidth, asteroid.image.asheight, asteroid.acx, asteroid.acy, 20, 20);
asteroid.acx += 1;
asteroid.acy += 1;
}
}
}
setInterval(spawnAsteroid, 2000);
setInterval(updateCanvas, 50);
spawnAsteroid();
<canvas id="asteroidCanvas"></canvas>

canvas with IEF, "static" variable and event

I wrote a program in JavaScript that displays 5 rectangles, one after each other with a ~1s delay.
As there is no static variable in Javascript as in C or other languages, I used an IIEF that returns a function in a variable draw to have an internal counter that is visible only in the function.
This code works perfectly.
let myCanvas=document.getElementById("my-canvas");
let draw=(function() {
let ctx = myCanvas.getContext('2d');
let counter=0;
return function() {
if (counter<5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(() => {window.requestAnimationFrame(draw)},1000);
}
}
})();
draw();
But now, I would like to fill the rectangle with a texture instead of the black color. Something like that :
let myPatternImg=new Image();
myPatternImg.onload = function() {
let myPattern=ctx.createPattern(myPatternImg,'repeat');
context.fillStyle=pattern;
... // code to draw rectangle
}
myPatternImg.src='pattern-file.png';
I do not how to do because when I initialize draw a function is returned directly. And this does not work with an onload event. I do not want a global variable for the counter. That is why I use an IIEF that returns a function to init the variable draw.
Any help would be appreciated. Thanks.
You can try use promises, to hide pattern creation:
function makePattern(ctx, src) {
return new Promise(function(resolve) {
let myPatternImg = new Image();
myPatternImg.onload = function() {
let myPattern = ctx.createPattern(myPatternImg, 'repeat');
resolve(myPattern);
};
myPatternImg.src = src;
});
}
let myCanvas=document.getElementById("my-canvas");
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function() {
makePattern(ctx, 'pattern-file.png').then(function(pattern) {
ctx.fillStyle = pattern;
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(() => {window.requestAnimationFrame(draw)},1000);
}
});
}
})();
it's always good idea the separate your code into functions that do one thing.
But this code make little of sens, you are executing in a loop draw and each will draw 5 rectangles, I think that what you really want is something like this:
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function() {
makePattern(ctx, 'pattern-file.png').then(function(pattern) {
ctx.fillStyle = pattern;
(function loop() {
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(() => {
window.requestAnimationFrame(loop);
}, 1000);
}
})();
});
}
})();
also this will fire each rectangle after 1000 seconds, if you want delay between each rectangle to be 1 second then use this:
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function() {
makePattern(ctx, 'pattern-file.png').then(function(pattern) {
ctx.fillStyle = pattern;
(function loop() {
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(loop, 1000);
}
})();
});
}
})();
if you call draw multiple times it will create pattern each time, you can make it request only once if you use this code:
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function(pattern) {
ctx.fillStyle = pattern;
(function loop() {
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(loop, 1000);
}
})();
}
})();
makePattern(ctx, 'pattern-file.png').then(draw);

How to create objects on runtime and move them?

I'm trying to create objects on my game update and move them. This is my banana object:
function Banana() {
this.height = 1.96;
this.width = 3.955;
this.pos_x = CENTER - this.width/2;
this.pos_y = -475;
this.banana_image = banana_image;
};
And this is the Move method:
Banana.prototype.move = function(){
if (this.pos_y > 500) {
//this.banana_image.parentElement.removeChild(this.banana_image);
}
this.height += ZOOM_RATE;
this.width += ZOOM_RATE;
this.pos_y += 3;
this.pos_x -= SIDES_RATE;
};
This is the Game Update part:
Game.update = function() {
this.player.move();
//creating bananas
if (objs.lenght <= 0) {
this.banana = new Banana();
} else {
for (i = 0; i < 10; i++) {
objs.push(new Banana());
}
}
//moving bananas
for (i = 0; i < objs.lenght; i++) {
this.objs[0].move();
}
};
Game Draw:
function Game.draw = function() {
this.context.drawImage(road, 0,0, rw, rh);
this.context.drawImage(
this.player.player_image,
this.player.pos_x, this.player.pos_y,
this.player.width, this.player.height);
this.context.drawImage(
this.banana.banana_image,
this.banana.pos_x, this.banana.pos_y,
this.banana.width, this.banana.height);
};
I tried to ask this to multiple people, but I can't find an answer for it.
Let's say you want to move the objects 10 times and then stop.
First you need to add a line to the start of Game.draw, so that it clears the canvas making you always start drawing from scratch:
this.context.clearRect(0,0,500,500); // clear canvas, adjust box size if needed
Then make a function to call both update and draw, and queue that function to be called again:
var count = 10;
function updateAndDraw() {
Game.update();
Game.draw();
count--;
if (count) requestAnimationFrame(updateAndDraw);
}
// start moving:
requestAnimationFrame(updateAndDraw);
The movement may go too fast to your liking, so then adjust the move method to make smaller changes, or use setTimeout instead of requestAnimationFrame (but that will make the animation less fluent).
Note that you have a few errors in your code, which you will need to fix first:
lenght should be length
function Game.draw = function() {: remove function before Game.draw.
... check the error messages you get in console.

Javascript & jQuery: how to make function infinite through animate callback?

have an object of a class Abon and then i want this object to move around the page.
a = new Abon();
a.init();
a.move();
the method move() contains:
function abon_move () {
var x = this.x;
var y = this.y;
var direction_x = Math.random()*5 - 5;
var direction_y = Math.random()*5 - 5;
var x_new = x + direction_x * this.movement_rate;
var y_new = y + direction_y * this.movement_rate;
console.log(x_new+" "+y_new)
$(".abonent."+this.id).animate( {
left:+x_new,
top:+y_new
}, 'slow', "linear", function() { this.move() });
}
All i want is that the method move (represented as function abon_move()) repeated again and again, after the animate stops. But the problem is that this.move() shown in callback has no connection to my object, because this in that place points to the HTML element, selected by jQuery.
UPD:
function Abon(id) {
...
this.move = abon_move;
...
}
Abon.prototype.move = abon_move;
And the actual method is the same, but with no callback in animate
then i try doing the following:
setInterval( a[0].move , 300); //doesn't work - says `this` members are undefined
setInterval( a[0].move() , 300); //works only one time and stops
Thank you for any help!
Try this :
function abon_move () {
var x = this.x;
var y = this.y;
var class = this;
...
}
Then, inside your jQuery animate, your can refer to your class using the variable class
Wrap the function abon_move() in a setTimeout call, as such: setTimeout(abon_move, 300); so it will run every 300 ms.

Categories

Resources