function initTimer(timeLeft) {
var Me = this,
TotalSeconds = 35,
Seconds = Math.floor(timeLeft);
var x = window.setInterval(function() {
var timer = Seconds;
if(timer === -1) { clearInterval(x); return; }
$('#div').html('00:' + (timer < 10 ? '0' + timer : timer));
Seconds--;
},1000);
}
I have this code. Everything works fine, when this tab is active in browser, but when I change tab and return in tab later it has problems. To be more precise, it Incorrectly displays the time.
I'd also tried setTimeout, but problem was the same.
One idea, which I have is: HTML5 Web Workers...
But here is another problem... browsers support.
can someone help to solve this problem?
How can I write setInterval, which works properly,even when tab is not active
Use the Date object to calculate time. Don't rely on a timer firing when you ask it to (they are NOT real-time) because your only guarantee is that it'll not fire before you ask it to. It could fire much later, especially for an inactive tab. Try something like this:
function initTimer(periodInSeconds) {
var end = Date.now() + periodInSeconds * 1000;
var x = window.setInterval(function() {
var timeLeft = Math.floor((end - Date.now()) / 1000);
if(timeLeft < 0) { clearInterval(x); return; }
$('#div').html('00:' + (timeLeft < 10 ? '0' + timeLeft : timeLeft));
},200);
}
initTimer(10);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="div"></div>
Note that by checking it more frequently we can make sure it's never off by too much.
JavaScript timers are not reliable, even when the tab is active. They only guarantee that at least as much time as you specified has passed; there is no guarantee that exactly that amount of time, or even anything close to it, has passed.
To solve this, whenever the interval fires, note what time it is. You really only need to keep track of two times: the current time, and the time that the previous interval fired. By subtracting the previous tick's time from the current tick's time, you can know how much time has actually passed between the two, and run your calculations accordingly.
Here's a basic outline of how something like this might look:
function initTimer(timeLeft) {
var Me = this,
TotalSeconds = 35,
Seconds = Math.floor(timeLeft),
CurrentTime = Date.now(),
PreviousTime = null;
var x = window.setInterval(function() {
var timer = Seconds,
timePassed;
PreviousTime = CurrentTime;
CurrentTime = Date.now();
timePassed = CurrentTime - PreviousTime;
if(timer < 0) { clearInterval(x); return; }
$('#div').html('00:' + (timer < 10 ? '0' + timer : timer));
Seconds = Seconds - timePassed;
},1000);
}
Related
I have a working timer, but it runs from 25 seg every time who the website is visited by a client, I want to synchronise it. F.E. if i visit my webpage in mY Pc, and when it show 15seg left, i visit it from other pc and i want it to show 15 left too.
function timerr(){
var initial = 25000;
var count = initial;
var counter;
var initialMillis;
function timer() {
if (count <= 0) {
clearInterval(counter);
return;
}
var current = Date.now();
count = count - (current - initialMillis);
initialMillis = current;
displayCount(count);
function displayCount(count) {
var res = count / 1000;
if (res<0.1){
document.getElementById("timer").innerHTML = "";
}
else{
tiempo = res.toPrecision(count.toString().length);
tiempo_corto = tiempo.slice(0,-1);
document.getElementById("timer").innerHTML = tiempo_corto;
}
}
clearInterval(counter);
initialMillis = Date.now();
counter = setInterval(timer, 10);
}
If you want everyone to have the same timer count down every 25 seconds and stop at the exact same time, then you can simply use timestamps to keep everything in sync. Here's an example countdown timer that'll restart every 6 seconds (from 5 to 0) and will hit zero at the exact same time for everyone (unless their computer clock is off).
const timerElement = document.getElementById('timer')
const TIMER_DURATION = 6
function step() {
const timestamp = Date.now() / 1000
const timeLeft = (TIMER_DURATION - 1) - Math.round(timestamp) % TIMER_DURATION
timerElement.innerText = timeLeft
const timeCorrection = Math.round(timestamp) - timestamp
setTimeout(step, timeCorrection*1000 + 1000)
}
step()
<p id="timer"></p> seconds
Try it - open this page up in two different tabs and run it. This is set up to automatically account for the fact that setTimeout doesn't always trigger at the delay you asked it to do so (it'll adjust the next setTimeout with a timeCorrection value to correct these issues).
The basic principle is that we're getting the current timestamp and modding it by the amount of time we want this timer to last (6 seconds in the above example). This value will always be the same for everyone, and will always be a number that ranges from 0 to 5. It will also be a number that counts up every second (which is why we then subtract (TIMER_DURATION - 1) from it, to cause the number to count down instead).
Trying to build a very simple Javascript countdown. However, whenever the tab is inactive, the countdown begins to lag and holds an incorrect count.
See jsfiddle here for example: https://jsfiddle.net/gbx4ftcn/
function initTimer(t) {
var self = this,
timerEl = document.querySelector('.timer'),
minutesGroupEl = timerEl.querySelector('.minutes-group'),
secondsGroupEl = timerEl.querySelector('.seconds-group'),
minutesGroup = {
firstNum: minutesGroupEl.querySelector('.first'),
secondNum: minutesGroupEl.querySelector('.second')
},
secondsGroup = {
firstNum: secondsGroupEl.querySelector('.first'),
secondNum: secondsGroupEl.querySelector('.second')
};
var time = {
min: t.split(':')[0],
sec: t.split(':')[1]
};
var timeNumbers;
function updateTimer() {
var timestr;
var date = new Date();
date.setHours(0);
date.setMinutes(time.min);
date.setSeconds(time.sec);
var newDate = new Date(date.valueOf() - 1000);
var temp = newDate.toTimeString().split(" ");
var tempsplit = temp[0].split(':');
time.min = tempsplit[1];
time.sec = tempsplit[2];
timestr = time.min + time.sec;
timeNumbers = timestr.split('');
updateTimerDisplay(timeNumbers);
if (timestr === '0000')
countdownFinished();
if (timestr != '0000')
setTimeout(updateTimer, 1000);
}
function animateNum(group, arrayValue) {
TweenMax.killTweensOf(group.querySelector('.number-grp-wrp'));
TweenMax.to(group.querySelector('.number-grp-wrp'), 1, {
y: -group.querySelector('.num-' + arrayValue).offsetTop
});
}
setTimeout(updateTimer, 1000);
}
I'm unsure whether the problem lies with the animation, or with the JS code itself.
For clarification: I want the countdown to continue when the tab is inactive, or to 'catch up with itself' when the tab comes back in to focus.
I know that setTimeout and setInterval can cause issues with inactive tabs, but I'm not entirely sure how to fix this.
Any help would be much appreciated!
For this you can use the HTML5 Visibility API for detecting if the browser tab is active or not. And use regular binding of event handlers for focus and blur for the browser window.
Basically you pause() the timeline when you blur out of the tab, and then play() when you give the tab refocus. Example of this in action:
http://codepen.io/jonathan/pen/sxgJl
// Set the name of the hidden property and the change event for visibility
var hidden, visibilityChange;
if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support
hidden = "hidden";
visibilityChange = "visibilitychange";
} else if (typeof document.mozHidden !== "undefined") {
hidden = "mozHidden";
visibilityChange = "mozvisibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
// If the page is hidden, pause the video;
// if the page is shown, play the video
function handleVisibilityChange() {
if (document[hidden]) {
tl.pause();
} else {
tl.play();
}
}
// Warn if the browser doesn't support addEventListener or the Page Visibility API
if (typeof document.addEventListener === "undefined" || typeof document[hidden] === "undefined") {
// do nothing or throw error via alert()
alert("This demo requires a browser, such as Google Chrome or Firefox, that supports the Page Visibility API.");
} else {
// Handle page visibility change
// Pause timeline
tl.pause();
}
HTML5 Visibility Docs:
https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
Regarding GreenSock Forum Topic:
http://forums.greensock.com/topic/9059-cross-browser-to-detect-tab-or-window-is-active-so-animations-stay-in-sync-using-html5-visibility-api/
Also in GSAP the equivalent for setTimeout() is delayedCall()
Provides a simple way to call a function after a set amount of time (or frames). You can optionally pass any number of parameters to the function too.
GSAP delayedCall(): http://greensock.com/docs/#/HTML5/GSAP/TweenMax/delayedCall/
//calls myFunction after 1 second and passes 2 parameters:
TweenMax.delayedCall(1, myFunction, ["param1", "param2"]);
function myFunction(param1, param2) {
//do stuff
}
I hope this helps!
before all, i have to mention that, those countdown animation is truly outstanding and elegant dear sir! well done...
Now to the answers:
as mentioned in many articles, and here is one of them:
setInterval(func, delay) does not guarantee a given delay between
executions. There are cases when the real delay is more or less than
given. In fact, it doesn’t guarantee that there be any delay at all.
Source: http://javascript.info/tutorial/settimeout-setinterval
and..
Most browsers apply performance improvements by reducing the priority
of several tasks on inactive tabs or even some portions of the page
that aren't on screen. Sometimes these improvements affects the
executions of Javascript intervals as well.
Source: How can I make setInterval also work when a tab is inactive in Chrome?
as you can see, they mentioned that setInterval does not guarantee a given delay between executions even when the tab is active, and lets just assume that setTimeout (the one that you used) is also the same because they are relatively is "the same"
so what is the solution to this problem?
well, what you actually can do is check how many times has actually elapsed between the event, something like this Codepen
EDIT: as you have requested, here is a fiddle of your countdown with the fix, and i have //commented on the changes i made to the fiddle, so it would hopefully make it easier for you to understand. So now, when you paired the countdown to other timer (ex. your phone's timer) you should get the same result even when the tab is inactive or the frame rate slowdown.
if you have enjoyed my answer please consider mark it as "the answer" or at least up vote it ;)
The simplest way to ensure the timer stays correct when a user moves off the tab and then returns is to use session storage - to store the original starting time:
on the first load - get the local current datetime and store into session storage (note that will only accept a string - so you will have to stringify the value and then parse it out again upon retrieval).
when the tab loses focus that set start time will still be stored as startTime in ss. When the tab regains focus - have a function that gets the new current datetime, gets the stored datedtime from session storage and calculates the difference. Then the timer can be updated to the new reduced time.
Eg if its 10:00 am on page load - set the startTime into ss. Then the user spends 1 minute on the site and goes offsite for 5 minutes. Upon returning - the above described process will determine the current time is 10:06 and determine that its 6 minutes later than the start time so can update the timer as such.
This avoids the need for intervals and timers etc. You can take the specificity down to ms if needed. And also - I like your fiddle too.
You can use this code. Each time the tab changes, it calculates the end time again and updates the counter.
let interval;
let duration=timeDifference(endTime(),nowDate())
updateTime();
$(window).focus(function () {
clearInterval(interval)
updateTime();
});
function updateTime() {
interval = setInterval(function () {
var timer = duration.split(':');
//by parsing integer, I avoid all extra string processing
var hours = parseInt(timer[0], 10);
var minutes = parseInt(timer[1], 10);
var seconds = parseInt(timer[2], 10);
--seconds;
minutes = (seconds < 0) ? --minutes : minutes;
if (minutes < 0) clearInterval(interval);
seconds = (seconds < 0) ? 59 : seconds;
seconds = (seconds < 10) ? '0' + seconds : seconds;
hours = (hours < 10) ? '0' + hours : hours;
//minutes = (minutes < 10) ? minutes : minutes;
$('.countdown').html(hours + ':' + minutes + ':' + seconds);
duration = hours + ':' + minutes + ':' + seconds;
}, 1000);
}
function nowDate() {
let date = new Date()
let time1 = new Date();
date = date.toISOString().slice(0, 10);
date = date.split('-');
return new Date(date[2] + '/' + date[1] + '/' + date[0] + " " + time1.getHours() + ":" + time1.getMinutes() + ":" + time1.getSeconds());
}
function endTime() {
let endTime = $('input[name=end_date]').val();
endTime = endTime.split(' ');
let date = endTime[0].split('-');
let time = endTime[1];
return new Date(date[2] + '/' + date[1] + '/' + date[0] + " " + time);
}
function timeDifference(date1,date2) {
var difference = date1.getTime() - date2.getTime();
var daysDifference = Math.floor(difference/1000/60/60/24);
difference -= daysDifference*1000*60*60*24
var hoursDifference = Math.floor(difference/1000/60/60);
difference -= hoursDifference*1000*60*60
var minutesDifference = Math.floor(difference/1000/60);
difference -= minutesDifference*1000*60
var secondsDifference = Math.floor(difference/1000);
return hoursDifference +":"+minutesDifference+":"+secondsDifference
}
I created a countdown timer in Javascript; it was successful, expect not complete. In fact, mathematically, it is correct, but Google Chrome's browser settings "pause" (for lack of a better term) SetInterval/Timeout, which means that if a user of my countdown program switches between tabs on their browser, then the execution of the function will not occur exactly at the set time limit.
I need help implementing this basic time logic from W3Schools:
http://www.w3schools.com/js/tryit.asp?filename=tryjs_timing_clock
<!DOCTYPE html>
<html>
<head>
<script>
function startTime() {
var today = new Date();
var h = today.getHours();
var m = today.getMinutes();
var s = today.getSeconds();
m = checkTime(m);
s = checkTime(s);
document.getElementById('txt').innerHTML =
h + ":" + m + ":" + s;
var t = setTimeout(startTime, 500);
}
function checkTime(i) {
if (i < 10) {i = "0" + i}; // add zero in front of numbers < 10
return i;
}
</script>
</head>
<body onload="startTime()">
<div id="txt"></div>
</body>
</html>
and this attempt to account for the browser SetInterval/Timeout interference: http://jsfiddle.net/7f6DX/31/
var div = $('div');
var a = 0;
var delay = (1000 / 30);
var now, before = new Date();
setInterval(function() {
now = new Date();
var elapsedTime = (now.getTime() - before.getTime());
if(elapsedTime > delay)
//Recover the motion lost while inactive.
a += Math.floor(elapsedTime/delay);
else
a++;
div.css("right", a);
before = new Date();
}, delay);
Thanks for any help that you can provide.
You should use real-world time to update your timer instead of relying on the accuracy of setInterval.
The w3schools example you gave does exactly this; every 500ms it grabs the current time, formats it, and updates the display. When the tab is inactive, this update may occur less frequently than 500ms (Chrome can slow it down to once every 1-2s), but nevertheless, when the update does occur, you will display correct information.
// countdown for 1 minute
countdown(60);
function countdown(seconds) {
// current timestamp.
var now = new Date().getTime();
// target timestamp; we will compute the remaining time
// relative to this date.
var target = new Date(now + seconds * 1000);
// update frequency; note, this is flexible, and when the tab is
// inactive, there are no guarantees that the countdown will update
// at this frequency.
var update = 500;
var int = setInterval(function () {
// current timestamp
var now = new Date();
// remaining time, in seconds
var remaining = (target - now) / 1000;
// if done, alert
if (remaining < 0) {
clearInterval(int);
return;
}
// format
var minutes = ~~(remaining / 60);
var seconds = ~~(remaining % 60);
// display
document.getElementById("countdown").innerHTML
= format(minutes) + ":" + format(seconds);
}, update);
}
function format(num) {
return num < 10 ? "0" + num : num;
}
<div id="countdown"></div>
Run this snippet and switch around to different tabs. Your countdown will be off by a maximum of 500ms (the update frequency).
For what it's worth, a similar idea can be applied to animations.
When designing an animation, you should have a formula for the position x as a function of time t. Your rendering clock (whether it is setInterval, setTimeout, or requestAnimationFrame) is not necessarily reliable, but your physics clock (real-world time) is. You should decouple the two.
Every time you need to render a frame, consult the physics clock for the time t, calculate the position x, and render that position. This is a really great blog post which goes into great detail on animations and physics, from which I borrowed the above idea.
I am creating an application that uses a timer to draw lines to several canvases while updating stats to DIVs. In order to create the smoothest animation possible I've had to set the setInterval() timer to the smallest delay possible, which from my understanding, is between 10-15 milliseconds across browsers.
The issue I am having is with the stopwatch that tracks the scenarios running time. The stopwatch itself works well enough as I have established using console.log, however, the setInterval() timer slows down when I try to update the DOM element. This has lead me to believe that in combination with the small timer tick delay there is a performance issue with the way I am updating the DOM element.
Code:
function draw() {
var drawTimer = setInterval(drawStuffTest, 15);
var timer = doc.getElementById('time');
var milliseconds = 0;
var seconds = 0;
var minutes = 0;
function updateTimer() {
//clear the old elapsed time
timer.innerHTML = "";
milliseconds += 15;
//increment seconds
if (milliseconds >= 1000) {
seconds++;
milliseconds = 0;
}
//increment minutes
if(seconds >= 60)
{
seconds = 0;
minutes++;
}
//Pad minutes and seconds with leading zeroes if either is lest than 10
var secondsString = ( seconds < 10 ? "0" : "" ) + seconds;
var minutesString = ( minutes < 10 ? "0" : "" ) + minutes;
var elapsedTime = minutesString + ":" + secondsString;
/* The timer works fine when I just use console.log to display the
timer however once I add the following statement to update the DOM
element the timer slows down */
//timer.innerHTML = elapsedTime;
console.log(elapsedTime);
}
Does anyone have any suggestions?
EDIT & UPDATE: for reference I rewrote the whole thing with only 1 timer and a second to time converter. Very clean and less complex. Here is the full code: http://pastebin.com/Hb6cBryL
I got this timer that I built out of javascript: http://powerpoint.azurewebsites.net/
It is quite simple code, but for the final implementation I would need to be able to pause the timer and restart it. I use 2 setIntervalls, 1 for the minutes that triggers every 60s and one for the seconds that triggers every second.
When I pause it I clear the intervals. However when I restart them the minutes restart at the 60 interval and aren't synced with the seconds anymore.
I probably implemented this in a horribly wrong way so I'd like to ask for your advice. 1 idea I had was to continue the inverval but avoid updating the variable and text on the page so that the minutes/seconds stay in sync< However this doesn't sound like an ideal solution to me. All tips are welcome :)
Js code:
var minutes = null, seconds = null, cnt, secs;
function settimer(frm) { if (minutes == null) { cnt = frm.timeinput.value - '1'; secs = '59';} };
function stop() {
clearInterval(minutes);
clearInterval(seconds);
minutes = null;
seconds = null;
document.getElementById("minutes").innerHTML = '00';
document.getElementById("seconds").innerHTML = '00';
}
function pause() {
clearInterval(minutes);
clearInterval(seconds);
}
function runtimer() {
event.preventDefault();
if (minutes == null) {
document.getElementById("minutes").innerHTML = cnt;};
minutes = setInterval(function () {
if (cnt == '0') { stop() } else { cnt -= 1; document.getElementById("minutes").innerHTML = cnt; };
}, 6000);
if (seconds == null) { document.getElementById("seconds").innerHTML = secs; };
seconds = setInterval(function () {
if (secs == '0') { secs = '59' } else { secs -= 1; document.getElementById("seconds").innerHTML = secs; };
}, 100);
}
You'll need to wrap them somehow, and recognise that you can't immediately get the timer's id.
function setIntervalWithOffset(fn, delay, offset) {
var o = {id: null, o_id: null};
o.o_id = window.setTimeout(function () {
o.id = window.setInterval(fn, delay);
}, offset);
return o;
}
function setTimeoutWithOffset(fn, delay, offset) {
var o = {id: null, o_id: null};
o.o_id = window.setTimeout(function () {
o.id = window.setTimeout(fn, delay);
}, offset);
return o;
}
To clear, use window.clearTimeout on obj.o_id and whichever type you set for obj.id.
You should also consider whether you'd be better off implementing a setTimeout loop rather than using setInterval so you don't have a chance of a cascade error
Sorry, I think that you are in a bad way. Why do you want to use two intervals to do the same thing? Intervals are asynchronous, you can not synchronize two intervals that runs independently.
You can achieve that with just one interval. To show seconds, you can just increment another variable each time that your counter reachs a threshold:
var counter = 0;
var seconds = 0;
var $interval = setInterval(function(){
counter++;
if (counter >= 6000) {
counter = counter - 6000;
seconds++;
}
, 10);
So, it will be more easy to stop/restart your interval.
You need to get the handle to the timer, in order to be able to reset it later, you can do like below:
timeoutHandle = setTimeout(function(){/*code*/}, 1000);
clearTimeout(timeoutHandle);
Take a look at this jsfiddle
Taken from : How do I stop a window.setInterval in javascript?