Im trying to create a ghost like in pacman (which is an image) which will appear at random positions. Here is my code
function ghost(){
while (true){
e1.style.top = Math.round(Math.random() * document.body.scrollHeight) + 'px';
document.body.appendChild(e1);
}
}
<body onload="gameLoop();" onkeydown="" onkeyup="moveSelection(event)" onload="ghost();">
I want it to run infintely side by side.
You cannot code an infinite loop like this in Javascript because it will just run forever, blocking the browser from doing anything else and thus effectively "hanging" the browser until the browser will eventually abort the script. The main Javascript execution in a browser is single threaded and thus if you loop forever like this, then nothing else can ever run.
Instead, you can use a setInterval() timer to update the position over time. If you can describe in words exactly what you're trying to accomplish, we could likely help more specifically with recommended code.
In addition, you can only have one onload attribute on the body tag so you can either call both functions from one attribute or create a new function that calls multiple things and refer to that new function in the onload attribute.
Here's a plain use of setInterval():
// something must set the e1 variable first
document.body.appendChild(e1);
setInterval(function() {
e1.style.top = Math.round(Math.random() * document.body.scrollHeight) + 'px';
}, 5000);
Note, per your code, this will cause the image to just jump around on the screen into random up/down positions every so often.
Related
Since JavaScript is sequential (not counting async abilities), then why does it not "seem" to behave sequential as in this simplified example:
HTML:
<input type="button" value="Run" onclick="run()"/>
JS:
var btn = document.querySelector('input');
var run = function() {
console.clear();
console.log('Running...');
var then = Date.now();
btn.setAttribute('disabled', 'disabled');
// Button doesn't actually get disabled here!!????
var result = 0.0;
for (var i = 0; i < 1000000; i++) {
result = i * Math.random();
}
/*
* This intentionally long-running worthless for-loop
* runs for 600ms on my computer (just to exaggerate this issue),
* meanwhile the button is still not disabled
* (it actually has the active state on it still
* from when I originally clicked it,
* technically allowing the user to add other instances
* of this function call to the single-threaded JavaScript stack).
*/
btn.removeAttribute('disabled');
/*
* The button is enabled now,
* but it wasn't disabled for 600ms (99.99%+) of the time!
*/
console.log((Date.now() - then) + ' Milliseconds');
};
Finally, what would cause the disabled attribute not take effect until after the for-loop execution has happened? It's visually verifiable by simply commenting out the remove attribute line.
I should note that there is no need for a delayed callback, promise, or anything asynchronous; however, the only work around I found was to surround the for-loop and remaining lines in a zero delayed setTimeout callback which puts it in a new stack...but really?, setTimeout for something that should work essentially line-by-line?
What's really going on here and why isn't the setAttribute happening before the for loop runs?
For efficiency reasons, the browser does not immediately layout and display every single change you make to the DOM instantly right when the change is made. In many cases, DOM updates are collected into a batch and then updated all at once at some later time (like when the current thread of JS finishes).
This is done because if a piece of Javascript is making multiple changes to the DOM, it is very inefficient to relayout the document and then repaint each change as it occurs and much more efficient to wait until the Javascript finishes executing and then repaint all the changes at once.
This is a browser-specific optimization scheme so every browser makes their own implementation decisions on exactly when to repaint a given change and there are some events that can cause/force a repaint. As far as I know, this is not an ECMAScript-specified behavior, just a performance optimization that each browser implements.
There are some DOM properties that require a finished layout before the property is accurate. Accessing these properties via Javascript (even just reading them) will force the browser to do a layout of any pending DOM changes and will usually also cause a repaint. One such property is .offsetHeight and there are others (though all in this category have the same effect).
For example, you can probably cause a repaint by changing this:
btn.setAttribute('disabled', 'disabled');
to this:
btn.setAttribute('disabled', 'disabled');
// read the offsetHeight to force a relayout and hopefully a repaint
var x = btn.offsetHeight;
This Google search for "force browser repaint" contains quite a few articles on this topic if you want to read about it further.
In cases where the browser still won't repaint, the other work-arounds are to hide, then show some element (this causes layout to be dirty) or to use a setTimeout(fn, 1); where you continue the rest of your code in the setTimeout callback - thus allowing the browser a chance to "breathe" and do a repaint because it thinks your current thread of Javascript execution is done.
For example, you could implement the setTimeout workaround like this:
var btn = document.querySelector('input');
var run = function() {
console.clear();
console.log('Running...');
var then = Date.now();
btn.setAttribute('disabled', 'disabled');
// allow a repaint here before the long-running task
setTimeout(function() {
var result = 0.0;
for (var i = 0; i < 1000000; i++) {
result = i * Math.random();
}
/*
* This intentionally long-running worthless for-loop
* runs for 600ms on my computer (just to exaggerate this issue),
* meanwhile the button is still not disabled
* (it actually has the active state on it still
* from when I originally clicked it,
* technically allowing the user to add other instances
* of this function call to the single-threaded JavaScript stack).
*/
btn.removeAttribute('disabled');
/*
* The button is enabled now,
* but it wasn't disabled for 600ms (99.99%+) of the time!
*/
console.log((Date.now() - then) + ' Milliseconds');
}, 0);
};
The browser doesn't render changes to the DOM until the function
returns. - #Barmar
Per #Barmar's comments and a lot of additional reading on the subject, I'll include a summary referring to my example:
JavaScript is single threaded, so only one process at a time can occur
Rendering (repaint & reflow) is a separate/visual process that the browser performs so it comes after the function finishes to avoid the potentially heavy CPU/GPU calculations that would cause performance/visual problems if rendered on the fly
Summarized another way is this quote from http://javascript.info/tutorial/events-and-timing-depth#javascript-execution-and-rendering
In most browsers, rendering and JavaScript use single event queue. It means that while JavaScript is running, no rendering occurs.
To explain it another way, I'll use the setTimeout "hack" I mentioned in my question:
Clicking the "run" button puts my function in the stack/queue of things for the browser to accomplish
Seeing the "disabled" attribute, the browser then adds a rendering process to the stack/queue of tasks.
If we instead add a setTimeout to the heavy part of the function, the setTimeout (by design) pulls it out of the current flow and adds it to the end of the stack/queue. This means the initial lines of code will run, then the rendering of the disabled attribute, then the long-running for-loop code; all in the order of the stack as it was queued up.
Additional resources and explanations concerning the above:
Event Loop
Painting
Reflow
I am learning JavaScript and have been developing a simple game that is essentially a balloon that you can move around on the screen. I managed to do keypresses etc with a lot of help and the balloon moved about just perfectly.
I now want to simulate gravity, by having the balloon move one pixel down the screen if the balloon image was above a value, i tried to do this with the following do while statement:
var balloon = document.getElementById("balloon");
var bottom = parseInt (balloon.style.bottom, 10);
do {
balloon.style.bottom = bottom + 50 + 'px';
}
while (bottom = bottom > 600) // Gravity
What I want this to do, is to check the code is working by making the balloon move up the page 1 pixel if the bottom value is less than 600.
I have stripped out all the code I used to make the balloon move.
If I could just see the balloon move slowly up the page I would be very happy, because then at least I know I can just switch the values round when I've added the movement back in.
The other answers address the issue of attempting an animation with an explicit loop. As they have pointed out, you should use timers.
Because it seemed like fun, I made you a simple example of how to use a timer to animate a balloon falling:
http://jsfiddle.net/dmuu9w97/
The key code is the following:
// Make balloon fall 1px every 10ms
setInterval(function() {
var bottom = getBalloonBottom();
if (bottom > 0)
balloon.style.bottom = bottom - 1 + "px";
}, 10);
For your while loop condition should be (bottom>600) . No need for '='
You are loading the variable bottom outside the loop. It will never change. If it is 610 at the start of the loop it will remain 610 because it is assigned only in line 2 of your code
While loop should probably be ....bottom = (bottom - 1) + 'px';
If you write a while loop like this, it will execute 10 times immediately and your baloon will be always stuck in 600
To solve 'stuck at 600' problem, you should use a timer:
Think about "how fast should the balloon fall". Then you can come up with some number like "5 pixels in 100 milliseconds".
Then write a function... call that function on a timer.
Check the setTimeout function here...
setTimeout method
It's not impossible to do with a do loop but I think you ought to abandon this explicit loop in favor of javascript's timer/timeline. Look into how to use window.setTimeout() where the body of your do loop becomes the body of the callback function AND a trailing call to window.setTimeout() passing the callback again with a delay of 1000/your-chosen-framerate milliseconds. Then you can also process keypress events in their own handlers for intentional movement.
If you use an explicit loop, you'll only get gravity because the loop should never end (just as gravity never stops pulling) and therefore the browser will never have a chance to call the keypress event handler.
Your timeout callback runs once, queues itself again, and terminates. That gives control back to the browser's javascript engine to process events or, if nothing else, run the callback function again after the requested delay.
requestAnimationFrame may be more appropriate than setTimeout in modern JS implementations. It usually leads to a smoother result for animations.
The problem is as such:
In a js and asm.js based multiplayer game I've got two loops.
One handles the actual game ticks, like unit position, velocity and combat.
The other handles rendering of this world onto the canvas for the user to see.
What I'd like to happen is when the processor/GPU(they made those the same thing on some machines now, can't say I'm happy about that) gets encumbered too much the rendering loop should skip and thus stop changing the canvas. I.e. freezing the game screen in a lag pike.
Meanwhile the little processing power left is used to successfully complete the actual game tick preventing de-synchronisation with other game clients.
(It's an RTS-like game when it comes to load so the user input instead of positions of all objects are sent over the net).
Failing this the client would have to be kicked by the other clients or all clients would have to pause for him to reconnect and resync. i.e. bad bad bad!
A sloppy makeshift way to do this would probably be by using timestamps and terminate the graphic loop if it won't be complete by a certain time. One would presumably do this by determining max execution time for the packet types on the stack of the loop and immediately terminate the loop if the "time to execute value" of all packets together is too great to be dealt with within the resource capacity the timestamps are indicating by slowdown measurement. Hell, maybe that's radical but perhaps even skip-terminating the graphic loop when any slowdown is detected just to be sure to avoid desync.
So priorotizing one loop over another(both handling ticks) and making the second one skip if a shortage in resource is detected to ensure the first one always completes it's tick within each timeframe(10 ticks per second here).
Any possibilities or best practice methods you guys can inform me on?
EDIT: Please focus on the ability to measure availability of cpu resources and the skipping/termination for one tick of the graphic loop if these resources would not available enough to finish both loops (i.e. if the loops won't finish in the 100ms timeframe after which the next loop tick should already be firing, don't start/terminate the graphics loop).
One solution would be to use a web worker to do your world update loop and then the normal javascript loop to do the render. You would need to hand the state back and forthright o /from the web worker but the render loop would only draw on the updated data.
An advantage is that you could still have reactionary display code on the main ui loop.
This also have the advantage of the fact that the web worker could be using a different core and with multiple web workers you could use multiple extra cores
Fo the logical loop, i would take setInterval, and for the paint - requestAnimationFrame. And even more - the callback at requestAnimationFrame also receives a timestamp, so you can track timestamps and skip single frame if some lack appear.
the processor is able to handle other tasks while also rendering the animation
This statement is wrong - processor can handle only one task, and requestAnimationFrame is not actually the Rendering, it is your callback - generic javascript. You can think about it like a setTimeout. The only difference is, that it tries to run the callback on next free framerate's frame. That's why it is much better than setTimeout. So for the animations you must use the requestAnimationFrame. Other good part about it is, when the webpage is in background(other tab opened). Then the callback wont be called until it comes to the foreground. This saves processor time, as nothing is calculated in that callback.
Going back to your question: You now but, that only one callback can be processed in a time, so if the processor is in a particular time busy with the logical function, then the callback of the animation loop won't be fired. In that case it calls 'lag'. But as I understood, it is actually the desired behavior - to give the logical callback function more time. But there is other side. What if your animation function is busy, when the time for logical function came to be fired? In this case it will be fired only when animation function ends. There is nothing to do about it. If your animation function is 'heavy', you could only try to split it for 2 frames. One frame - prepare everything for render, the second one - render.
But anyway, you never become millisecond-perfect interval or timeout in javascript. As it want be called until event-loop is not free. To get the idea:
var seconds = 0;
setInterval(function(){ seconds++; var x = 10e8; while(--x); }, 1000);
Depends on you CPU, but after 10 seconds time, variable 'seconds' will be much less then 10.
And one more thing, if you really rely on time, then it is safer to use Date.now() to synchronize next logical tick:
var setLogicalLoop = (function(){
var _startedAt,
_stop,
_ms;
function frame(){
if (_stop === true)
return;
// calculations
var dt = Date.now() - _startedAt,
diff = dt % _ms;
setTimeout(frame, ms - diff);
};
return function (callback, ms){
_startedAt = Date.now();
_stop = false;
setTimeout(frame, ms);
return function(){
_stop = true;
};
};
});
// -> start
var stopLoop = setLogicalLoop(myFunction, ms);
// -> stop
stopLoop();
I have a function that runs on a click event that uses javascript's setIterval for some of my animations (i'm doing a game) so the problem is that if a user clicks while the animation is still displaying (setInterval is still executing) the setInterval is stacking up in the event stack or that is what I found out thus either crushing my game or running twice as fast (the animation). My question is is there any way to prevent event stacking? I do not want the setInterval to stack up on the previous setInterval and so on. I know that I could use clearInterval function like so:
var timerInterval = setInterval(drawPlayerRunning, 50);
clearInterval(timerInterval);
but it does not really work as I want it to, because what if user clicks many times while the function is still is executing, the clearInterval will only get rid of last event of the event stack leaving all the previous ones still in the "game". Any idea how to prevent this event stack up, or at least removing them efficiently?
You can create a flag that monitors the interval state:
1)
var isIntervalInProgress = false;
setInterval(function()
{
if ( isIntervalInProgress )
return false;
isIntervalInProgress = true;
drawPlayerRunning();
isIntervalInProgress = false;
}, 50);
or just a timeout that will run itself once it's finished:
2)
var func = function()
{
setTimeout(function()
{
drawPlayerRunning();
func();
}, 50)
}
whichever you like
You want to use requestAnimationFrame. It is designed with games in mind, and if your code happens to be too slow, it will reduce your frame rate accordingly (from 60 fps to 30 fps for instance). But it won't stack-up events.
Edit: Sorry, I think I misunderstood your question. Let me try again.
You should have only one draw function which is called every few milliseconds (set the interval up with requestAnimationFrame(draw)).
A click should not add a new interval, but rather create a floatingAnimation object and add it to the list of objects to render. All animation objects will be rendered by the draw function everytime the browser calls draw. In the arguments passed to draw, there will be a timestamp. Use this timestamp minus the creation date of floatingAnimation to determine how to draw the floating thing above the character.
I'm calling a javascript function that sets the opacity of an iframe an unknown amount of times in rapid succession. Basically this tweens the alpha from 0 to 100.
here is the code
function setAlpha(value)
{
iframe.style.opacity = value * .01;
iframe.style.filter = 'alpha(opacity =' + val + ')';
}
My problem is that for the first time it is working in ie (7) and not in firefox (3.02). in Firefox I get a delay and then the contentdocument appears with an opacity of 100. If I stick an alert in it works, so I'm guessing it is a race condition (although I thought javascript was single threaded) and that the setAlpha function is being called before the last function has finished executing.
Any help would be greatly appreciated. I've read the 'avoiding a javascript race condition post' but I think this qualifies as something different (plus I can't figure out how to apply that example to this one).
The issue is that most browsers don't repaint until there is a pause in the javascript execution.
This can be solved by using setTimeout, as others have suggested. However, I recommend using something like jQuery, or any of the javascript libraries to do animations. Running setTimeout 100 times is a bad idea because the length of the animation will vary based on the browser and speed of the user's computer. The correct way to do animations, is to specify how long they should last and check the system time to determine how far the animation should progress.
function fadeIn(elem,animation_length) {
var start = (new Date()).getTime();
var step = function() {
window.setTimeout(function() {
var pct = ((new Date()).getTime() - start)/animation_length;
elem.style.opacity = Math.min(pct,1);
if (pct < 1)
step();
},20);
};
step();
}
[edit:] The code above is only to illustrate how to do animations based on the system clock instead of simple intervals. Please use a library to do animations. The code above will not work on IE, because IE uses "filter:opacity(xx)" instead of "opacity". Libraries will take care of this for you and also provide nice features such as completion events, and the ability to cancel the animation.
Javascript doesn't run across multiple threads so you're safe from race conditions (ignoring upcoming Worker thread support in Safari and Firefox :D ).
Simple question, how are you calling setAlpha multiple times, firefox, safari and opera all coalesce style sheet updates -- eg. they won't repaint or even recalc style info while js is running unless they have to. So they will only paint if JS has completed.
So if you're doing
while(...) setAlpha(...)
they won't update, you'll probably need to use setTimeout to trigger multiple distinct calls to update the style.
An alternative would be to use a library such as jQuery, mootools,etc that i vaguely recall provide a simplified mechanism to do these types of animations and transitions. As an added bonus i believe at least a few libraries will also use webkit transition and animation css rules when available (eg. Safari, and i think the latest firefox builds)
[edit: caveat: i haen't actually used any of these libraries, i only read about what they're supposed to do. My sites render the same in lynx as any other browser because i couldn't design my way out of a paper bag :D ]
Are you using setTimeout or a tight loop? If you're using just a loop to call the function, then switch to using setTimout.
example:
function setAlpha(value)
{
iframe.style.opacity = value * .01;
iframe.style.filter = 'alpha(opacity =' + val + ')';
if(value < 100 ) {
setTimeout(function () {setAlpha(value+1)},20);
}
}
setAlpha(0);
Because you see, it's not just javascript that's single threaded. It's the whole damn browser. If your javascript goes into a tightloop, you hang the whole browser. So the browser pauses waiting for javascript to finish, and doesn't even have a chance to update the screen, while your code is rapidly changing some dom values.
Some browsers are smart enough to delay changes to the DOM until the call stack is empty.
This is a generally a smart thing to do. For example, if you call a function that changes an element to yellow, and immediately call a function that changes the same element back to it's original state, the browser shouldn't waste time making the change, since it should happen so quickly as to be imperceptible to a user.
The setTimeout(func, 0) trick is commonly used to force Javascript to delay execution of func until the call stack is empty.
In code:
function setAlpha(opacity){
some_element.style.opacity = opacity;
}
/**
* This WON'T work, because the browsers won't bother reflecting the
* changes to the element's opacity until the call stack is empty,
* which can't happen until fadeOut() returns (at the earliest)
**/
function fadeOut(){
for (var i=0; i<10; i++){
setAlpha(0.1*i);
}
}
/**
* This works, because the call stack will be empty between calls
* to setAlpha()
**/
function fadeOut2(){
var opacity = 1;
setTimeout(function setAlphaStep(){
setAlpha(opacity);
if (opacity > 0){
setTimeout(setAlphaStep, 10);
}
opacity -= 0.1;
}, 0);
}
All this boils down to being a wonderful excuse to use one of many javascript libraries that handle this tricky stuff for you.
Edit: and here's a good article on the tricky Javascript call stack