How to pause/play a Javascript loop when a key is pressed? - javascript

Using Javascript/jQuery, how can I pause (or resume) the following loop when the "P" key is pressed?
(function() {
var arr = [...],
len = arr.length;
(function doProcess(i) {
if (i) {
console.log(len - i);
/* do something with arr[len - i] */
setTimeout(function() { doProcess(--i); }, 20000);
}
})(len);
})();

Pausing and resuming is fairly complex. What you really have to do is this:
Start a process and store its timeout ID.
Store the time when you ran that process.
On keypress, clear the timeout using its timeout ID.
Store the unfinished time in another variable.
On next keypress, set the timeout using that unfinished time.
Set the following timeouts to the original intended delay.
Here's a more generalized jsFiddle example I whipped up.
var counterOn = true;
var delay = 3000;
var lastRun;
var tempDelay;
var intervalId;
function decrementCounter() {
// do something
lastRun = new Date();
timeoutId = setTimeout(decrementCounter, delay);
}
function toggleCounter() {
var curTime = new Date();
counterOn = !counterOn;
if (counterOn) {
lastRun = curTime.valueOf() + tempDelay - delay;
timeoutId = setTimeout(decrementCounter, tempDelay);
} else {
clearTimeout(timeoutId);
tempDelay = delay - (curTime.valueOf() - lastRun);
}
}
$(document).keydown(function(e) {
if (e.which === 80) {
toggleCounter();
}
});
decrementCounter();

You'll want to keep track of how much time has passed with your timer (see here: javascript: pause setTimeout();) and call clearTimeout on whatever event you want to stop, then call setTimeout again with your remaining time left once whatever event unpauses is fired again.

Related

How to use the clearInterval() function to us?

Cannot terminate the setInterval I created in launch. It works until the time is up. I want to use clearInterval (interval) operation in next() function and prev() function. How should I do this? When I click forward, I want clearInterval(interval) to run this, but I couldn't.
function launch() {
thisTimeline = document.getElementsByClassName('story-active-' + start)[0];
var maxtime = 5000;
var incremental = 100;
var actualtime = 0;
var interval = setInterval(function() {
actualtime += incremental;
var percentage = Math.ceil((100 / maxtime) * actualtime);
thisTimeline.style.width = percentage + '%';
if (percentage == 100) {
clearInterval(interval);
thisTimeline.style.width = "0%";
}
}, incremental);
}
function next() {
// Set previous video timeline to 100% complete
thisTimeline.style.width = '100%';
// Advance play count to next video
start++;
// If next video doesn't exist (i.e. the previous video was the last) then close the Social Story popup
if (start >= defaults.playlist.length) {
setTimeout(function() {
close();
return false;
}, 400);
} else {
// Otherwise run the next video
launch(start);
}
}
function prev() {
if (start != 0) {
thisTimeline.style.width = '0%';
}
// Subtract play count to previous video
start--;
// If next video doesn't exist (i.e. the previous video was the last) then close the Social Story popup
if (start < 0) {
start = 0;
return false;
} else {
// Otherwise run the previous video
launch(start);
}
}
This is an extension of #lagoCalazans comment.
What he is saying is that in your variable "interval" is created in your launch function. You need to make "interval" global in order to clear your setInterval.
Ex:
let interval = null; //global
function launch() {
let tempInterval = setInterval(function() {
//whatever code
},100);
interval = setInterval(function(){
console.log("Hello");
}, 100);
}
function clear() {
//Since interval is global I can clear it when I call clear();
clearInterval(interval);
}
As you can see in the launch function "tempInterval" is limited to the scope of launch, therefore cannot be accessed anywhere else, but now since "interval" is global it can be accessed in any function.
Your code seems a bit incomplete, so for illustrative purposes only I will assume you encapsulate those functions in a higher order function (like an IIFE) and will avoid writing that (also, some kind of global state or variable would do for an example).
First of all, setInterval will return an id which you would use later, so if you want to use it within next and prev, you need that value to be available to them.
So, in your example, you should declare interval outside launch, and assign a value to it inside:
let interval
function launch() {
// ...
interval = setInterval(function() { ... })
}
and then use interval wherever you want.
launch, next and prev are three separate functions. They do not reference the same interval because they don't share scope. Raise the scope of the interval variable.
let interval = ''; // declared here, interval can be accessed by all functions
function launch() {
// ...
// remove the var before interval
interval = setInterval( ... )
}
function next() {
// ...
// remove the var before interval
interval = setInterval( ... )
}
function prev() {
// ...
// remove the var before interval
interval = setInterval( ... )
}

what is the most optimized between

i'm using mousemove event and as it executes each time possible, I want to optimize this and I think about these two possibilities:
ok = true;
function mousemove(e)
{
if(ok == true)
{
ok = false;
window.setTimeout(function(){ ok = true; }, 1000/60);//60 FPS
//Code here
}
}
and
lastTime = +new Date();
function mousemove(e)
{
if(+new Date() - lastTime > 1/60)//60 FPS
{
lastTime = +new Date();
//Code here
}
}
So is it better to use window interval (or timeout) or use a timestamp?
(if you have another idea, I take!)
Thank you!
Within the event I would simply update a variable (e.g. position of the mouse) and within a second event (e.g. requestAnimationFrame) I would read that variable and reset it. So in the next loop I check if it has a valid value and compute it again (cause the user moved the mouse further) or it is still not set (cause the user didn't move the mouse any further).
let currentPosition = null;
function onPaint() {
if(currentPosition !== null) {
let paintPosition = currentPosition;
currentPosition = null;
// ToDo: update visualization by using paintPosition...
}
window.requestAnimationFrame(onPaint);
}
function onMouseMove(e) {
currentPosition = e.position;
}
onPaint();
Using an interval means having a function that is executed 60 times per second, even if the mouse is not being used.
Checking if the right period has passed within the mouse event callback is only executed when needed.
So I'd discard the interval approach (same for timeout):
(function () {
var time=+new Date();
myElement.addEventListener('mousemove', function () {
if (time- (+new Date()) < 60) return;
time=+new Date();
/* Here my logic*/
});
})()

Image player issue with jQuery

I've made a jQuery player for images Demo Link.
It changes the screens with provided intervals and draws touches on it. Now, I want to implement pouse, play functionality.
When I click on play button to stop screen playing, I call FlowPlaye.stop() method:
FlowPlayer.prototype.stop = function() {
$(".fp-pause").removeClass("fp-pause").addClass("fp-play");
clearInterval(this.screenIntervalId);
clearInterval(this.timeIntervalId);
clearInterval(this.touchIntervalId);
$('.fp-progress').stop();
this.isAnimated = false;
return false;
}
And at the second time FlowPlayer.play():
FlowPlayer.prototype.play = function() {
var fp = this; // Obj refers to the FlowPlayer itself such as "this"
fp.isAnimated = true;
console.log(typeof this.screenIndex)
console.log(this.screenIndex)
fp.screenIndex = typeof this.screenIndex == 'number' ? this.screenIndex : 0;
fp.render(fp.screens[fp.screenIndex]);
fp.initTimeline(fp.duration);
fp.screenIntervalId = setInterval(function() {
if (fp.screenIndex == fp.screens.length - 1) {
console.log("the end of screens");
clearInterval(fp.screenIntervalId)
return;
}
++fp.screenIndex;
fp.render(fp.screens[fp.screenIndex]);
}, fp.screens[fp.screenIndex].delay)
}
The problem is that when I do this, the screen playing intervals are messing (try to stop video at 20th second and restore). I need to save state of player, but I don't know how.
I think using 3 different timers is making this unnecessary difficult. If you refactor it into 1 unified timer, pausing (and other playback controls) would be quite easy.
Separate your keyframe events into separate functions:
function setImage(img) {...}
function showTouch(x, y) {...}
function hideTouch() {...}
On startup, convert your screens array to something like this:
var keyframes = [
{ time:0, func:setImage, args:['http://...']},
{ time:1000, func:showTouch, args:[10, 30]},
{ time:3000, func:hideTouch, args:[]},
...
];
Set up a single timer for playback:
var time = 0,
next = 0,
isPaused = false,
interval;
function timer() {
if (isPaused) {
return;
}
var nextKeyframe = keyframes[next];
time += 100;
if (time >= nextKeyframe.time) {
nextKeyframe.func.apply(this, nextKeyframe.args);
next += 1;
if (next === keyframes.length) {
clearInterval(interval);
}
}
}
Now, you have an easily controllable playback:
// play / replay - reset time and next, then start the timer
time = 0;
next = 0;
interval = setInterval(timer, 100);
// seek - just set a new time, and find the next keyframe
time = 1500;
for (next = 0; keyframes[next].time < time && next < keyframes.length; next++) {}
// pause - the timer stays on, but won't do anything
isPaused = true;
// stop
clearInterval(interval);
Note: The snippets are untested, may have some typos in them. I just wanted to demonstrate the process of making it cleaner / more controllable.

Increase the value of a number in an element every x milliseconds

So I have this simple HTML:
<span id="badge">0</span>
I want the number 0 to increase by 1 every x milliseconds. How do I do that with Javascript (with or without jQuery)?
Thanks a bunch - I'm new to this :)
You should do this:
<script>
var $badge = $('#badge'); // cache
setInterval(function () {
var value = parseInt($badge.html());
value++;
$badge.html(value);
}, 1000);
</script>
Assuming 1000 milliseconds.
function increment() {
document.getElementById("badge").value = Number(document.getElementById("badge").value) + 1;
setTimeout("increment()",3000);
}
increment()
Every of the answers I see here has the same drawbacks:
performance issue because of selecting the DOM element every ms cycle. Especially when using a heavy library as jQuery.
setInterval() is probably the tool designed for that functionality, but not reliable. It can diverge a lot from the real time, especially when using a small interval. If you want exactly x executions per second, you may google for some timing libraries.
I would code:
var textNode = document.getElementById(badge).firstChild;
var start = Date.now();
window.setInterval(function update() {
textNode.data = Math.round((new Date()-start)/ms);
}, ms);
If you don't want to start at 0, it will be trivial to add an offset (determined before the loop begins), eg.
var start = Date.now() - (textNode.data * ms || 0); // NaN catching, implicit number cast
Something like this?
var millisecs = 10;
setInterval(function() {
var $badge = $('#badge');
$badge.text(parseInt($badge.text())++);
}, millisecs);
http://jsfiddle.net/iambriansreed/MPP8n/3/
Check this http://www.w3schools.com/js/js_timing.asp
A little more about timers:
// setting a variable for your timer will allow you the ability to "turn it on and off"
var tmrChangeI;
// setTimeout is a function to initiate a function once after given amount of milisecs
// whereas setInterval will continue a function until cancled every so many milisecs
// the following wil "turn on" your timer
tmrChangeI = setInterval(function() {
var $badge = $('#badge');
$badge.html($badge.html() + 1);
}, 500); // 500 will = every half of a second
// to "turn off" timer
clearInterval(tmrChangeI);
// or set a bool to test and use timeout to repeat till bool is false
var tmrBool = true;
// establish function to execute
function tmrFunc() {
var $badge = $('#badge');
$badge.html($badge.html() + 1);
if (tmrBool) tmrChangeI = setTimeout(function() { tmrFunc(); }, 500); // 500 will = every half of a second
};
// execute function, begin timer
tmrChangeI = setTimeout(function() { tmrFunc(); }, 500);
// clear via bool allowing one more execution
tmrBool = false;
// clear by force possibly stoping next execution,
// tho in this manner it may be too late if timer is very short
// and maybe overriden by bool still being true, this is not safest
// but is example of how to use setTimeout
clearTimeout(tmrChangeI);
You can use setInterval.
var $badge = $('#badge');
setInterval(function () {
$badge.html(parseInt($badge.html()) + 1);
}, 1);​//Specify the milliseconds here, right it will update the value every 1 millisecond
Working demo - http://jsfiddle.net/8FMZh/
You could create a Jquery plugin, so you can reuse whenever you need.
$.fn.increment= function(options) {
var $this = $(this);
var coef = options.coef;
var speed = options.speed;
var value = 0;
setInterval(function(){
value = value + coef ;
$this.html(value);
}, speed);
};
And in your main javascript file :
$("#badge").increment({coef: 1, speed:1000});
working demo : http://jsfiddle.net/8FMZh/102/

Is there a way to check if a var is using setInterval()?

For instance, I am setting an interval like
timer = setInterval(fncName, 1000);
and if i go and do
clearInterval(timer);
it does clear the interval but is there a way to check that it cleared the interval? I've tried getting the value of it while it has an interval and when it doesn't but they both just seem to be numbers.
There is no direct way to do what you are looking for. Instead, you could set timer to false every time you call clearInterval:
// Start timer
var timer = setInterval(fncName, 1000);
// End timer
clearInterval(timer);
timer = false;
Now, timer will either be false or have a value at a given time, so you can simply check with
if (timer)
...
If you want to encapsulate this in a class:
function Interval(fn, time) {
var timer = false;
this.start = function () {
if (!this.isRunning())
timer = setInterval(fn, time);
};
this.stop = function () {
clearInterval(timer);
timer = false;
};
this.isRunning = function () {
return timer !== false;
};
}
var i = new Interval(fncName, 1000);
i.start();
if (i.isRunning())
// ...
i.stop();
The return values from setTimeout and setInterval are completely opaque values. You can't derive any meaning from them; the only use for them is to pass back to clearTimeout and clearInterval.
There is no function to test whether a value corresponds to an active timeout/interval, sorry! If you wanted a timer whose status you could check, you'd have to create your own wrapper functions that remembered what the set/clear state was.
I did this like below, My problem was solved. you should set the value like "false", when you clearTimeout the timer.
var timeer=false;
----
----
if(timeer==false)
{
starttimer();
}
-----
-----
function starttimer()
{
timeer_main=setInterval(activefunction, 1000);
timeer=true;
}
function pausetimer()
{
clearTimeout(timeer_main);
timeer=false;
}
Well you can do
var interval = setInterval(function() {}, 1000);
interval = clearInterval(interval);
if (typeof interval === 'undefined'){
...
}
but what are you actually trying to do? clearInterval function is an always success function and it will always return undefined even if you call it with a NaN value, no error checking in there.
You COULD override the setInterval method and add the capability to keep track of your intervals. Here is an untestet example to outline the idea. It will work on the current window only (if you have multiple, you could change this with the help of the prototype object) and this will only work if you override the functions BEFORE any functions that you care of keeping track about are registered:
var oldSetInterval = window.setInterval;
var oldClearInterval = window.clearInterval;
window.setInterval = function(func, time)
{
var id = oldSetInterval(func, time);
window.intervals.push(id);
return id;
}
window.intervals = [];
window.clearInterval = function(id)
{
for(int i = 0; i < window.setInterval.intervals; ++i)
if (window.setInterval.intervals[i] == id)
{
window.setInterval.intervals.splice(i, 1);
}
oldClearInterval(id);
}
window.isIntervalRegistered(id)
{
for(int i = 0; i < window.setInterval.intervals; ++i)
if (window.setInterval.intervals[i] == func)
return true;
return false;
}
var i = 0;
var refreshLoop = setInterval(function(){
i++;
}, 250);
if (isIntervalRegistered(refrshLoop)) alert('still registered');
else alert('not registered');
clearInterval(refreshLoop);
if (isIntervalRegistered(refrshLoop)) alert('still registered');
else alert('not registered');
The solution to this problem: Create a global counter that is incremented within your code performed by setInterval. Then before you recall setInterval, test if the counter is STILL incrementing. If so, your setInterval is still active. If not, you're good to go.

Categories