I have an infinity scroll style page I'm working on, (for an example of infinity scroll, click here and scroll down) which runs a function called update() every time the user scrolls, among other things.
I could use setInterval instead and check every x millis, however with a timeout that calls itself, the page responds quicker and more reliably.
Basically, update() adds some content to the page, then, if that content isn't enough to let you scroll down more, sets a timeout to call update() again. If you only scrolled one pixel, this would be fine, but of course, when you scroll it sends tens or hundreds of scroll events. This becomes a problem because there will be tens (or hundreds) of timeouts running at once. Having too many timeouts kills the consistent animation of adding new content I was going for.
So I wanted to check for if a timeout was already waiting to execute using the ID I had, however there doesn't seem to be a function for this is JavaScript. Is there a way to check whether a timeout ID has executed yet or not? If not, are there alternatives? I have JQuery.
...Or should I just make an interval and sacrifice the responsiveness?
When the function fires, clear the interval and set the variable you're holding the initial in to null. Then check for a null value. If you're doing a bunch of these, you could store them in an array by the id you have.
eg:
var intervals = [];
if(intervals[id] != null){
intervals[id] = setTimeout(some_func, 1000);
var some_func = function(){
// do stuff
clearTimeout(intervals[id]);
intervals[id] = null;
}
}
Related
In my game i'm trying to make a variable go up gradually, so i need to be able to sleep a certain amount of time before I increment the variable again.
1) Use a timer. This will allow you to execute one off delayed events, or execute that function call repeatedly.
An example, as the phaser website basically gives you examples of everything if you search for it,
http://phaser.io/examples/v2/time/basic-timed-event
Also, from the docs
http://phaser.io/docs/2.4.8/Phaser.Timer.html
Should you need to repeat, note the function "loop" (I would post the link, but i don't have enough reputation yet).
2) The alternative is simply to tie in to Phaser's game tick. Within your state (if this is where you are executing you variable incrementation), create an update function (this will be called every game update). You can access time since last update.
Look up the class Phaser.Timer (again, i cannot post the link).
See the properties elapsed and elapsedMS. You could use this to manually track time elapsed since your last incremental event (behind the scenes, this is basically what phaser tweens or timed events are doing).
eg:
var timeSinceLastIncrement = 0;
function update()
{
// Update the variable that tracks total time elapsed
timeSinceLastIncrement += game.time.elapsed;
if (timeSinceLastIncrement >= 10) // eg, update every 10 seconds
{
timeSinceLastIncreemnt = 0;
// Do your timed code here.
}
}
Note, that option 1 is cleaner, and likely to be the preferred solution. I present option 2 simply to suggest how this can be done manually (and in fact, how it is generally done in frameworks like phaser behind the scenes).
So my problem is I want to insert a custom animation but I don't want to ruin my gameloop.
My initial gameloop is stated here:
function init(){
if(!gameOver){
if(resetInterval>-1) clearInterval(resetInterval);
createBlock();
resetInterval = setInterval(moveDownCheck,gameSpeed);
}
}
My game is a tetris like game except instead of dropping tetriminos, I drop 2x1 blocks of different color. The moveDownCheck method checks if there are any blocks under my 2x1 block and then drops it by 1 row. This works fine until I have a block hanging without a block underneath since the 2x1 blocks are connected. I want to insert a drop animation that would take about a second and drop the hanging block by the same gameSpeed increment.
Here is my attempt that doesn't work:
function moveFallingDown(){
fbDownFlag = false;
clearInterval(resetInterval);
fbInterval = setInterval(function(){
fallingBlock.row++;
console.log("Dropped One Row");
},gameSpeed);
while(landscape[fallingBlock.row+1][fallingBlock.col]==0){
console.log("Waiting to Drop Falling Block");
}
clearInterval(fbInterval);
resetInterval = setInterval(moveDownCheck,gameSpeed);
}
Here I am attempting to wait for the function(){fallingBlock.row++;}, but my game just crashes and in the console "Dropped One Row" yet "Waiting to Drop Falling Block" will display thousands of times.
I guess I shouldn't be using a while loop here, but the only other solution I can think of would be a complete rework of my design, or nested setInterval methods which would just make my head hurt too much.
You can't do this with a while loop, you need to use a recursive function. window.setTimeout would work, however this seems like a good use case for requestAnimationFrame. Check it out here: https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame
You can use that to call your moveFallingDown method, and check how long has passed since the last animation frame to move your animation the right amount according to the game speed, by using the high precision timestamp passed to the requestAnimationFrame callback.
#Adrien Delessert's advice is correct but I'll just add that you're definitely confusing setTimeout and setInterval.
First of all you don't need a while loop.
setInterval IS a loop. So, if you wanted to use it you'd need to wrap (basically) the whole game in a method that moves the game forward (whatever that means) and pass that to setInterval.
However, what you're doing (and this is actually not a terrible approach) is to use it as a means to animate specific things. In that case you should have a recursive(ish) function that keeps calling setTimeout when it's done, if the conditions for another round of animation are met.
I haven't ever used requestAnimationFrame, but that does sound like a much more elegant approach to the problem.
The reason it's better is that it leverages the browser's own refresh timer (about 60 times per second) and will slot your animation frames in along with its own refresh.
So yes, you will listen for that callback and then react to it as necessary. If 60x per second is too fast, you'll need to put in a % based counter for how many of those frames you wish to actually react to.
I have a function that positions elements of my page. For example:
refresh_positions = function(){
set_pos(arrow_div,mouse_x,mouse_y);
set_pos(title_tiv,window_width/2,20);
};
This, obviously, must run continously, as fast as possible, in order for the elements to always be positioned correctly. But how?
If I set a fast timer like setInterval(refresh_positions,10), slower computers will freeze. If I set a slower timer, faster computers will have a worse experience. I also have a concern with battery drainage in mobile devices. What's the right way of positioning elements based on a function?
Try to use setTimeout or window.requestAnimationFrame, this ensures that new function call will only be queued when the previous function finished to run. In this way the Browser UI wont freeze that fast if the function takes longer than the interval you planned for.
var interval = <so many seconds should it take>;
function update(){
//do you stuff here
setTimeout( update, interval );
//or
window.requestAnimationFrame( update );
}
the second approach with window.requestAnimationFrame has the advantage that the is triggered up to sixty times a second if possible, or less if the browser cannot run it that fast. Also the callback is not triggered if the tab or the browser looses focus.
I'd like to continuously execute a piece of JavaScript code on a page, spending all available CPU time I can for it, but allowing browser to be functional and responsive at the same time.
If I just run my code continuously, it freezes the browser's UI and browser starts to complain. Right now I pass a zero timeout to setTimeout, which then does a small chunk of work and loops back to setTimeout. This works, but does not seem to utilize all available CPU. Any better ways of doing this you might think of?
Update: To be more specific, the code in question is rendering frames on canvas continuously. The unit of work here is one frame. We aim for the maximum possible frame rate.
Probably what you want is to centralize everything that happens on the page and use requestAnimationFrame to do all your drawing. So basically you would have a function/class that looks something like this (you'll have to forgive some style/syntax errors I'm used to Mootools classes, just take this as an outline)
var Main = function(){
this.queue = [];
this.actions = {};
requestAnimationFrame(this.loop)
}
Main.prototype.loop = function(){
while (this.queue.length){
var action = this.queue.pop();
this.executeAction(e);
}
//do you rendering here
requestAnimationFrame(this.loop);
}
Main.prototype.addToQueue = function(e){
this.queue.push(e);
}
Main.prototype.addAction = function(target, event, callback){
if (this.actions[target] === void 0) this.actions[target] = {};
if (this.actions[target][event] === void 0) this.actions[target][event] = [];
this.actions[target][event].push(callback);
}
Main.prototype.executeAction = function(e){
if (this.actions[e.target]!==void 0 && this.actions[e.target][e.type]!==void 0){
for (var i=0; i<this.actions[e.target][e.type].length; i++){
this.actions[e.target][e.type](e);
}
}
}
So basically you'd use this class to handle everything that happens on the page. Every event handler would be onclick='Main.addToQueue(event)' or however you want to add your events to your page, you just point them to adding the event to the cue, and just use Main.addAction to direct those events to whatever you want them to do. This way every user action gets executed as soon as your canvas is finished redrawing and before it gets redrawn again. So long as your canvas renders at a decent framerate your app should remain responsive.
EDIT: forgot the "this" in requestAnimationFrame(this.loop)
web workers are something to try
https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers
You can tune your performance by changing the amount of work you do per invocation. In your question you say you do a "small chunk of work". Establish a parameter which controls the amount of work being done and try various values.
You might also try to set the timeout before you do the processing. That way the time spent processing should count towards any minimum the browsers set.
One technique I use is to have a counter in my processing loop counting iterations. Then set up an interval of, say one second, in that function, display the counter and clear it to zero. This provides a rough performance value with which to measure the effects of changes you make.
In general this is likely to be very dependent on specific browsers, even versions of browsers. With tunable parameters and performance measurements you could implement a feedback loop to optimize in real-time.
One can use window.postMessage() to overcome the limitation on the minimum amount of time setTimeout enforces. See this article for details. A demo is available here.
I'm using OpenX at work, and one of my boss requirements is a expandable banner. For that (and made a horrible simplification of the whole story) I made this script.
function retro(){
var acs = jQuery('#trial_center').height() - 5;
jQuery('#trial_center').css('height', acs + 'px');
}
jQuery(document).ready(function(){
jQuery("#trial_center").mouseover(function(){
setTimeout("jQuery('#trial_center').css('height', '500px')", 1000);
})
jQuery("#trial_center").mouseleave(function(){
var c = 89;
while (c > 0) {
setTimeout("retro()", 1000);
c--;
}
})
});
The problem I have is in the mouseleave event: the original idea was to made this loop several times (89 times), and each time, decrease the height of the banner until it get his original size. Why this? Because my boss want an "effect", and this effect must be in sync with the customer's flash.
The problem is that instead of decrease his size progressively, apparently the script made all the operations an "after" the sum of setTimeout calls, updated the page. So, the result is exactly as the banner shrinks one time from the expanded size to the original size.
I don't know what is wrong with this, or if exists other more intelligent solution.
Any help will be very appreciate.
Thanks in advance
Your loop setting the timeout is just setting 89 timers for one second later than the loop runs, and the loop will run in milliseconds — so they'll all fire about a second later. That doesn't sound like what you want to do.
Two options for you:
1. Use animate
jQuery's animate function seems like it does what you want. You can tell jQuery to animate the size change, and you tell it how long to take to do so:
jQuery('#trial_center').animate({
height: "500px" // Or whatever the desired ending height is
}, 1000);
That will animate changing the height of the container from whatever it is at the point that code runs to 500px, across the course of 1,000 milliseconds (one second). Obviously you can change the duration to whatever you like.
2. Set up the timer loop manually
If for whatever reason you don't want to use animate, you can do this manually (of course you can; jQuery can't do anything you can't do yourself, it just makes things easier). Here's how to set up a timer loop:
jQuery("#trial_center").mouseleave(function(){
var c = 89;
// Do the first one right now, which will schedule the next
iteration();
// Our function here lives on until all the iterations are
// complete
function iteration() {
// Do one
retro();
// Schedule this next unless we're done
if (--c > 0 {
setTimeout(iteration, 100); // 100ms = 1/10th second
}
}
});
That works because iteration is a closure over c (amongst other things). Don't worry about the term "closure" if it's unfamiliar, closures are not complicated.
Separately: You're using mouseover to set the height of the trial_center element a second later; you probably wanted mouseneter rather than mouseover. mouseover repeats as the mouse moves across it.
Off-topic:
It's best not to use strings with setTimeout; just pass it a function reference instead. For example, instead of
setTimeout("retro()", 1000);
you'd use
setTimeout(retro, 1000); // No quotes, and no ()
And for the other place you're using, instead of
setTimeout("jQuery('#trial_center').css('height', '500px')", 1000);
you'd use
setTimeout(function() {
jQuery('#trial_center').css('height', '500px');
}, 1000);