JavaScript for loop issue when drawing object - javascript

Somehow i did my research and found out javascript is single threaded !.
I've been trying to figure out , how to make animation via for loop in javascript.
This is what i've done so far.
I'm trying to draw an object on a canvas using javascript. It run the loop it doesn't go step by step. It's just run the loop and draw once instead of 10 times and it ignores the timeout function.
In a single instance , it drew (P/S. Ignore the multiple version cause i was testing it so i removed the context.clearRect(0, 0, context.canvas.width, context.canvas.height); :
JS:
// down button click
down.onclick = function() {
if (!imgLoaded) return;
flag=false;
setTimeout(function() {
for(var i = 0; i < 15 ; i++) {
posY += 10;
context.drawImage(img, posX, posY );
}}, 9);
// call next step

That's not the way setTimeout works. The way you wrote it, setTimeout calls its first parameter one time, after 9 ms, then loops with no delay.
You could use the setInterval function (don't forget to removeInterval after the 15th iteration) if you want to be called a regular interval.
Note that in both case (setTimeout, setInterval), given delay is only indicative.
What you might want to use is the Window.requestAnimationFrame() function, which is the usual way to do animation in the browser.

Related

How should I organize my update/draw logic when using requestAnimationFrame?

The JavaScript code for my HTML5 game has the following structure:
// <body onload="load()">
function load() {} // Load all images then call init()
function init() {} // Get all images ready for the game logic then call animate()
function animate() {} // Use requestAnimationFrame(), update() and drawing()
function update() {} // Update the game logic
function drawing() {} // Render the images on canvas
The issue lies inside animate(). I'm not finding any consistent sources around the web on how to organize requestAnimationFrame(), update() and drawing() in it.
I tried to elaborate it by myself, but the game did run in pratically any approach, like passing either animate(), update() or drawing() as an argument to requestAnimationFrame(), or having requestAnimationFrame() at either the beginning or the end of the function, or having any of these functions in any order, or one function inside another, etc.
That, however, doesn't mean anything is fine. Some of those arrangements result in issues that I'd find out only later, like when testing in a different computer or at a different frame rate. And then I have to go back to the code to try another approach.
So, how should I organize that? I'd appreciate if you can present me a proper algorithm, and even more if you have any good sources on teaching about it.
Use requestAnimationFrame to call animate repeatedly. animate calls update then draw. That's basically it. To have more control of time since you don't control the intervals exactly, it makes sense to pass the last time that animate was invoked. Maybe event the delta time that has passed since, makes more sense. Then you can use delta time to calculate distance given a speed and so on.
Here's an example of a game loop which is explained here:
var now,
dt = 0,
last = timestamp(),
step = 1/60;
function frame() {
now = timestamp();
dt = dt + Math.min(1, (now - last) / 1000);
while(dt > step) {
dt = dt - step;
update(step);
}
render(dt);
last = now;
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
There are many resources online. Here's a decent one for beginners https://www.sitepoint.com/quick-tip-game-loop-in-javascript/

rapid clickings slows down the timer downdown on android chrome

I have a mini game where user is required to click rapidly on a button in a given time (8 seconds). There is a countdown (to the mili seconds). While i was trying on an android touchscreen (using android table OS6 i think), i am experiencing the timer to slow down while rapidly clicking. Is there a way to avoid or improve the performance for this? This is the countdown timer which i assume could do an improvement? Not sure does GSAP helps to replace the setinterval in this matter?
function countDownNow(){
// var initial = 800;
var initial = 8000;
var count = initial;
var counter; //10 will run it every 100th of a second
function timer() {
if (count <= 0) {
console.log(done)
clearInterval(counter);
return;
}
count--;
displayCount(count);
}
function displayCount(count) {
var res = count / 100;
//document.getElementById("countdown").innerHTML = res.toPrecision(count.toString().length) ;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#fff';
ctx.font = '90px "Conv_DIN-Bold"';
var text_title = "first";
ctx.fillText(res.toPrecision(count.toString().length), 15, canvas.height / 2 + 35);
}
counter = setInterval(timer, 10);
displayCount(initial);
}
HTML
<!-- Skeleton html -->
<div id="countdown"></div>
<!-- this clickme button is the button where its being used for user to rapidly click it-->
<div class="clickme"></div>
As said in my answer to your last post, setIntervals should be avoided, especially when you have to use them for precise timing and especially when you're already using GSAP. There's no reason to use them if you're using GSAP.
For functionality like you have here there is no reason why it should ever perform poorly. The two biggest performance hits are 1) using a bunch of setIntervals and 2) having functions within functions.
When you have functions within functions they are created every time the function is ran. If those outer functions are called more than once, you are often times creating the inner functions more times than you need to. To avoid doing that, move the inner functions outside of the outer functions and use parameters to pass in variables if need be. (in terms of memory management, a minor improvement would be to move variables that don't change outside of the functions as well but that's much less important to do)
Another note is that the intervals that you are creating are all going to overwrite each other but you're not killing off the old ones. So you should kill off any that were created before since the output of them won't be seen (because it's covered by the new ones) anyway.
Altogether you get something like this: demo.

How to get a circle to move when you click on it in javascript?

I'm making a whack-a-mole game based on a 3x3 grid for one of my classes but i'm having trouble getting the mole(an ellipse made using p5.js) to move to a different grid square on click. I want it to automatically move after 7 seconds which i achieved using function sleep(milliseconds), but if the player clicks the mole before the 7 seconds is up then I want it to switch grid squares right then. This is my code for mousePressed and moveCircle.
function mousePressed() {
var distance = int(dist(mouseX, mouseY, moleX, moleY));
if(distance <= circleDiameter/2 ) {
moveCircle();
score += 1;
}
document.getElementById('playerScore').innerHTML=score;
}
function moveCircle(){
var randomX = [110, 260, 410];
var randomY = [95, 245, 395];
var moleX = random(randomX);
var moleY = random(randomY);
}
Your sleep() function is not how you should handle timing in P5.js, or in JavaScript in general.
Your sleep() function causes your program to become completely unresponsive, which is bad for a lot of reasons. One problem is that your event code will not trigger, which is the problem you're seeing.
Instead, you need to use the millis() function or the frameCount variable to handle your timing without blocking your whole program. Here's a small example:
function setup() {
createCanvas(200,200);
background(32);
}
function draw(){
// 60 frames is 1 second
if(frameCount % 60 == 0){
ellipse(random(width), random(height), 25, 25);
}
}
This code uses the frameCount variable along with the % modulus operator to check whether 1 second has elapsed. In other words, this program draws a circle every second.
You need to do something similar to move your mole, which will allow your event code to be fired even while the mole is "waiting".
int(dist(mouseX, mouseY, moleX, moleY))
This int keyword does not make sense. If you want it to be a whole number, use the Math.round() function (or just round() in p5js).
Try console.loging the distance variable.

Why would the canvas2d context no longer fill ellipses?

I'm working on a javascript game that simulates gravitational forces. It uses the HTML5 canvas element to draw 2D ellipses for planets. I test my game in Google Chrome. Here's a link to the game: http://gravitygame.hostingsiteforfree.com/index.php?page=playHTML
Up until May 24th, it worked just fine. However, after Chrome upgraded from 26.0.1410.64 to 27.0.1453.94, the filled ellipses are sometimes not drawn. It doesn't happen every time I load my game, and I've never gotten it to break while running locally.
Here's a screenshot of the game working:
And here's a screenshot that shows it not filling the ellipses:
I can't tell what's happening. I'll include the portion of the loop that draws all of the planets. I've modified it for readability.
var i = bodies.length;
while(i--){
var I = bodies[i];
var planetRad = (I.width/2)*_scale;
if(_showTrails){
//draw the planet's trail
}
if(//the planet is completely off the screen){
//draw a red planet on the edge of the screen
ctx.beginPath();
ctx.arc(nX, nY, 2.5, 0, TWOPI);
ctx.fillStyle = offScreenColor;
ctx.fill();
ctx.strokeStyle = offScreenOutline;
ctx.stroke();
}
else{
//draw planet
ctx.beginPath();
ctx.arc(nX, nY, (I.width/2)*_scale, 0, TWOPI);
ctx.closePath();
ctx.fillStyle = I.bodyColor;
ctx.fill();
}
if(_showMotionVector){
//draw a line from the center of a planet showing the direction and speed it's travelling
ctx.strokeStyle = motionColor;
ctx.beginPath();
ctx.moveTo(I.getScX(), I.getScY());
ctx.lineTo(I.motion.x * _scale * 12 + I.getScX(), I.motion.y * _scale * 12 + I.getScY());
ctx.stroke();
}
}
Why would it suddenly break on occasion?
I took a look at your online code and discovered you are using setInterval for the animation loop.
This is most likely the reason as if the code is not able to finish calling the calcs etc. you run the risk of stacking calls - for context that means you can have path's that reset each other.
Try first to replace setInterval with setTimeout. You will of course need to retrigger it again from within the code - better yet, put everything in a function with a setTimeout at the end of that function, ie.:
function animate() {
//... calcs and redraws which you have in setInterval
setTimeout(animate, 0);
}
animate();
I use 0 for timeout here for this test. setTimeout/setInterval won't sync to screen refresh rate in any case.
If that works then you know the reason. The next step would be to replace it with requestAnimationFrame, but let me know how it goes.
In an attempt to illustrate the problem we can look at this illustration:
Each block represent a function within the loop, and one loop is one color. Remember that setInterval calls at fixed intervals while setTimeout calls relative to when it's called. In this example the functions perform within the time budget so everything goes well.
In the next illustration:
the spending is outside the budget so setInterval is called again and queues up next call to the second loop before the first has finished. When the queue is processed between the calls you end up risking having two functions working on the context at the "same time" (or comes in a different order than you might expect).
Javascript is of course single-threaded so they do not execute at the same time, but one is held at wait - if the first block for next queue is called before the last block has time to be called then the first block will modify the context and perhaps even change the path before the last call of the previous call is invoked. Over time the lag-behind will increase and potentially (unless some extra available processing resources resolves the queue now and then - on a busy system this is less likely to happen) become worse and worse as more stacking occur.
Ie, in this case you could have lines added to the context with beginPath() before arc got filled.
(hope that made any sense...)
Using setTimeout will prevent this as it won't be executed before all calls in the animation loop has returned. The better option is to use requestAnimationFrame as this will call in sync with the screen-refresh rate, also when possible. It's more low-level and therefor also more efficient.
Another path (no pun intended) is to use Web-workers to do the calculations. This will be multi-threaded and can increase overall performance as a web-worker does not affect the UI thread.

recursive function and setTimeout() problem

I have a script that draws a bunch of lines on canvas, but it's pretty intense so while rendering freezes browser for a few seconds. I added setTimeout() so that the browser wouldn't freeze and it effectively messed up my script. It's difficult to explain how, so I have two examples online:
Without setTimeout() : http://www.modwebsolutions.com/test1
With setTimeout() : http://www.modwebsolutions.com/test2
Note, that I only change a single line in the whole script, that is line 69:
without setTimeout(): vLoop();
with setTimeout(): setTimeout(vLoop,1);
The problem here, as hinted at by others, is that you are drawing the lines a quadrant at a time. As soon as the SetTimeout method is called and the first vLoop returns, the code carries on running into the next drawVertical which changes all the global variables and so on.
What you need to do is synchronise how you're calling vLoop and how you are changing the globals.
This is basically the solution:
Replace ...
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0);
... with ...
var q = new Array();
q[0] = [c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2+50,y];
q[1] = [c,step,stepInt,bigStep,xStart,xEnd,y/2+50,y];
q[2] = [c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0];
q[3] = [c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0];
drawQuadrant(q, 0);
Replace your drawVertical function with ...
function drawQuadrant(q, i)
{
var r = q[i];
c__ = r[0];
step__ = r[1];
stepInt__ = r[2];
bigStep__ = r[3];
xStart__ = r[4];
xEnd__ = r[5];
yStart__ = r[6];
yEnd__ = r[7];
vLoop(q,i);
}
change the vLoop function prototype to look like this ...
function vLoop(q,i)
and finally replace your recursive vLoop call (from within vLoop) with ...
if ((xStart__ > 0) && (xStart__ < window.innerWidth))
{
setTimeout( function(){vLoop(q,i)}, 1 );
}
else if (++i < 4)
{
setTimeout( function(){drawQuadrant(q,i)}, 1 );
}
The last block is where it ensures that the quadrants are not stepping over each other.
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0);
Your calling 4 recursive functions of vLoop at once. The problem here is that setTimeout is non-blocking where as recursion is blocking. So basically you now have all 4 of these drawVertical functions running in parallel rather then in sequence.
The other problem is that all 4 refer and mess with global state and your entire program breaks.
What's happening is what setTimeout() delays all that execution until later. Unfortunately, by that point your global variables have all moved to their ending positions from the initial loop, since it completed before the first line was drawn.
If you moved your timeout further up (so the shared variables you're using aren't affected until draw time) you could achieve what you're after, for example:
setTimeout(function() {
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0);
});
Then this would work (but it's dangerous, order isn't absolutely garunteed!)
You can see a working example here.

Categories

Resources