javascript countdown stutter - javascript

I have a countdown like this one:
var countdown = {
startInterval: function() {
var count = 600
var countorig = 600;
var currentId = setInterval(function() {
var min = (count - (count % 60)) / 60;
var sec = count % 60;
if (sec < 10) {
$('#timer').html(min + ':0' + sec);
} else {
$('#timer').html(min + ':' + sec);
}
$('#time').val(countorig - count);
if (count == 0) {
$('#form').submit();
}--count;
}, 1000);
countdown.intervalId = currentId;
}
};
It works. But if I load the page, the countdown starts but it stutter it is not "round" like a clock is.
JSFiddle.

setInterval isn’t exact. You should use Dates instead, to get an accurate time, and then choose an interval of less than one second to get a smoother clock. Here’s a demo!
var countdown = {
startInterval: function() {
var count = 600;
var start = new Date(); // The current date!
var currentId = setInterval(function() {
var difference = Math.max(0, count - (new Date() - start) / 1000 | 0);
var min = difference / 60 | 0;
var sec = difference % 60;
$('#timer').text(min + ':' + (sec < 10 ? '0' : '') + sec);
$('#time').val(difference);
if(count === 0) {
$('#form').submit();
}
}, 200);
countdown.intervalId = currentId;
}
};

It's never a good idea to assume your timers are exact. Instead, use delta timing.
var startTime = new Date().getTime();
setInterval(function() {
var elapsed = new Date().getTime()-startTime;
console.log("Been running for "+Math.floor(elapsed/1000)+" seconds");
},25);

That is because setInterval is not meant to be a high resolution timer. It will NOT hit every 1000 milliseconds on the dot. You might have swings as much as 20 to 30 milliseconds in either direction, resulting in a clock that is off.

Using Date.now(), this is a quick example of a countdown function ( x is milliseconds )
function countdown(x){
var o = {future: Date.now()+x, last:-1, i:null}; // object so we can pass by-ref if req.
o.i = setInterval( function() { // use o.i so we can clear interval
var remain = o.future - Date.now(),
secs = Math.floor( remain / 1000 ),
mins = 0;
if( remain < 0 ){ // finished, do whatever
return clearInterval(o.i); // then clear & exit
}
if( secs === o.last ) return; // skip this iteration if it's not been a second
o.last = secs; // else update last time
// do whatever you want for this new second
if( secs > 59 ) mins = Math.floor( secs / 60 ), secs = secs % 60;
console.log(
(mins < 10 ? '0'+mins : mins) + ':' +
(secs < 10 ? '0'+secs : secs) + ' remain.'
);
}, 100);
}
If you know it wont be used in IE, consider adding o as an argument to the callback function in the interval and also as the last argument to setInterval (so it is passed to the callback as first arg), which means closure is independent => callback can be defined anywhere.

Related

Minutes not quite in sync with seconds

I have a timer which I am testing, it seems there is a bit of drift between when the minute countdown goes down by 1 and seconds whenever it reaches 59 seconds ()ie every minute:-
How can I alter this so they are both in sync?
my code is the following:-
$(document).ready(function() {
function now() {
return window.performance ? window.performance.now() : Date.now();
}
function tick() {
var timeRemaining = countdown - ((now() - initTick) / 1000);
timeRemaining = timeRemaining >= 0 ? timeRemaining : 0;
var countdownMinutes = Math.floor(timeRemaining / 60);
var countdownSeconds = timeRemaining.toFixed() % 60;
countdownTimer.innerHTML = countdownMinutes + ":" + countdownSeconds;
if (countdownSeconds < 10) {
countdownTimer.innerHTML = countdownMinutes + ":" + 0 + countdownSeconds;
}
if (timeRemaining > 0) {
setTimeout(tick, delay);
}
}
var countdown = 600; // time in seconds until user may login again
var delay = 20; // time (in ms) per tick
var initTick = now(); // timestamp (in ms) when script is initialized
var countdownTimer = document.querySelector(".timer"); // element to have countdown written to
setTimeout(tick, delay);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="timer"></div>
js fiddle: https://jsfiddle.net/robbiemcmullen/cer8qemt/1/
The issue is the precision is not the same for minutes and seconds.
You need to round to the nearest second before /60 / %60.
Consider: exactly 9 mins remaining:
var x = 540;
console.log(x.toFixed() % 60, Math.floor(x / 60));`
Output is: (0,9)
Then consider the call 20 ms later:
var x = 539.980;
console.log(x.toFixed() % 60, Math.floor(x / 60));
the output is now: (0, 8).
So the seconds haven't changed (yet) but the minute does.
Here is a version using setInterval and removing the use of .toFixed ()
Why do you use an interval of 20ms and not 1 second?
//method for countdown timer
$(document).ready(function() {
function now() {
return window.performance ? window.performance.now() : Date.now();
}
function tick() {
var timeRemaining = countdown - elapsedTime;
var countdownMinutes = Math.floor(timeRemaining / 60);
var countdownSeconds = timeRemaining % 60;
countdownTimer.innerHTML = countdownMinutes + ":" + countdownSeconds;
if (countdownSeconds < 10) {
countdownTimer.innerHTML = countdownMinutes + ":" + 0 + countdownSeconds;
}
++elapsedTime;
return timeRemaining;
}
var countdown = 600;
var elapsedTime = 0;
var timeRemaining;
// countdown: time in seconds until user may login again
//var delay = 20;
// delay: time (in ms) per tick
var initTick = now(); // initTick: timestamp (in ms) when script is initialized
var countdownTimer = document.querySelector(".timer");
// countdownTimer: element to have countdown written to
var interval = setInterval(function() {
if(tick() <= 0) {
clearInterval(interval);
}
}, 1000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="timer"></div>
js fiddle https://jsfiddle.net/ud3wm8t1/

Accurate Javascript Timer

I am trying to build an accurate countdown timer that shows mins and seconds left. I have tried 2 approaches. Approach 1 uses the setTimer function and calculates the drift. For that approach, some values get skipped and some values get repeated. Approach 2 yields all of the necessary values, but the values are not getting printed to the screen at even intervals (tested in repl.it). How can I make a timer that is both accurate and prints all of the values ?
Approach1:
function countTime(duration) {
var expected = 1;
var secsLeft;
var startT = new Date().getTime();
var oneSecond = 1000;
var expected = startT + oneSecond;
window.setTimeout(step, oneSecond);
function step() {
var nowT = new Date().getTime();
var drift = nowT - expected;
if (drift > oneSecond) {
console.log("drift is over 1 second long!");
}
console.log('drift is ' + drift);
var msDelta = nowT - startT;
var secsLeft = duration - Math.floor(msDelta / 1000);
console.log("secsLeft" + secsLeft);
if (secsLeft === 0) {
++count;
console.log("cleared");
} else {
expected += oneSecond;
setTimeout(step, Math.max(0, oneSecond - drift));
}
}
}
countTime(60);
Approach2:
function countTime(duration) {
var expected = 1;
var secsLeft;
var inter;
var startT = new Date().getTime();
inter = setInterval(function() {
//change in seconds
var sChange = Math.floor((new Date().getTime() - startT) / 1000);
if (sChange === expected) {
expected++;
secsLeft = duration - sChange;
console.log("seconds Left" + secsLeft);
}
if (secsLeft === 0) {
window.clearInterval(inter);
console.log("cleared");
}
}, 100);
}
countTime(60);
Consider using requestAnimationFrame.
function countTime(duration) {
requestAnimationFrame(function(starttime) {
var last = null;
function frame(delta) {
var timeleft = Math.floor(duration - (delta - starttime)/1000),
minutes = Math.floor(timeleft/60),
seconds = timeleft%60;
if( timeleft > 0) {
if( last != timeleft) {
console.log("Time left: "+minutes+"m "+seconds+"s");
last = timeleft;
}
requestAnimationFrame(frame);
}
}
frame(starttime);
});
}
countTime(60);
This will be precise to within the framerate of the browser itself :)
function(){
date = get the date
curSeconds = compute the number of seconds to be displayed
if(oldSeconds!=curSeconds) then refresh display and do oldSeconds = curSeconds;
}
Call this function quite often, the more you call it, the more accurate your timer will be. I advise you to use requestAnimationFrame() instead of setTimout() it will be called 60 times per second (period 16ms) since it is the refresh rate of most displays, it is the maximum visible "accuracy". Also it won't be called when page is not visible.
Simple, clean, no drift over long periods of time.
It also handle not being called for a while.

Find elapsed time in javascript

I'm new to JavaScript and I'm trying to write a code which calculates the time elapsed from the time a user logged in to the current time.
Here is my code:-
function markPresent() {
window.markDate = new Date();
$(document).ready(function() {
$("div.absent").toggleClass("present");
});
updateClock();
}
function updateClock() {
var markMinutes = markDate.getMinutes();
var markSeconds = markDate.getSeconds();
var currDate = new Date();
var currMinutes = currDate.getMinutes();
var currSeconds = currDate.getSeconds();
var minutes = currMinutes - markMinutes;
if(minutes < 0) { minutes += 60; }
var seconds = currSeconds - markSeconds;
if(seconds < 0) { seconds += 60; }
if(minutes < 10) { minutes = "0" + minutes; }
if(seconds < 10) { seconds = "0" + seconds; }
var hours = 0;
if(minutes == 59 && seconds == 59) { hours++; }
if(hours < 10) { hours = "0" + hours; }
var timeElapsed = hours+':'+minutes+':'+seconds;
document.getElementById("timer").innerHTML = timeElapsed;
setTimeout(function() {updateClock()}, 1000);
}
The output is correct upto 00:59:59 but after that that O/P is:
00:59:59
01:59:59
01:59:00
01:59:01
.
.
.
.
01:59:59
01:00:00
How can I solve this and is there a more efficient way I can do this?
Thank you.
No offence, but this is massively over-enginered. Simply store the start time when the script first runs, then subtract that from the current time every time your timer fires.
There are plenty of tutorials on converting ms into a readable timestamp, so that doesn't need to be covered here.
var start = Date.now();
setInterval(function() {
document.getElementById('difference').innerHTML = Date.now() - start;
// the difference will be in ms
}, 1000);
<div id="difference"></div>
There's too much going on here.
An easier way would just be to compare markDate to the current date each time and reformat.
See Demo: http://jsfiddle.net/7e4psrzu/
function markPresent() {
window.markDate = new Date();
$(document).ready(function() {
$("div.absent").toggleClass("present");
});
updateClock();
}
function updateClock() {
var currDate = new Date();
var diff = currDate - markDate;
document.getElementById("timer").innerHTML = format(diff/1000);
setTimeout(function() {updateClock()}, 1000);
}
function format(seconds)
{
var numhours = parseInt(Math.floor(((seconds % 31536000) % 86400) / 3600),10);
var numminutes = parseInt(Math.floor((((seconds % 31536000) % 86400) % 3600) / 60),10);
var numseconds = parseInt((((seconds % 31536000) % 86400) % 3600) % 60,10);
return ((numhours<10) ? "0" + numhours : numhours)
+ ":" + ((numminutes<10) ? "0" + numminutes : numminutes)
+ ":" + ((numseconds<10) ? "0" + numseconds : numseconds);
}
markPresent();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="timer"></div>
Here is a solution I just made for my use case. I find it is quite readable. The basic premise is to simply subtract the timestamp from the current timestamp, and then divide it by the correct units:
const showElapsedTime = (timestamp) => {
if (typeof timestamp !== 'number') return 'NaN'
const SECOND = 1000
const MINUTE = 1000 * 60
const HOUR = 1000 * 60 * 60
const DAY = 1000 * 60 * 60 * 24
const MONTH = 1000 * 60 * 60 * 24 * 30
const YEAR = 1000 * 60 * 60 * 24 * 30 * 12
// const elapsed = ((new Date()).valueOf() - timestamp)
const elapsed = 1541309742360 - timestamp
if (elapsed <= MINUTE) return `${Math.round(elapsed / SECOND)}s`
if (elapsed <= HOUR) return `${Math.round(elapsed / MINUTE)}m`
if (elapsed <= DAY) return `${Math.round(elapsed / HOUR)}h`
if (elapsed <= MONTH) return `${Math.round(elapsed / DAY)}d`
if (elapsed <= YEAR) return `${Math.round(elapsed / MONTH)}mo`
return `${Math.round(elapsed / YEAR)}y`
}
const createdAt = 1541301301000
console.log(showElapsedTime(createdAt + 5000000))
console.log(showElapsedTime(createdAt))
console.log(showElapsedTime(createdAt - 500000000))
For example, if 3000 milliseconds elapsed, then 3000 is greater than SECONDS (1000) but less than MINUTES (60,000), so this function will divide 3000 by 1000 and return 3s for 3 seconds elapsed.
If you need timestamps in seconds instead of milliseconds, change all instances of 1000 to 1 (which effectively multiplies everything by 1000 to go from milliseconds to seconds (ie: because 1000ms per 1s).
Here are the scaling units in more DRY form:
const SECOND = 1000
const MINUTE = SECOND * 60
const HOUR = MINUTE * 60
const DAY = HOUR * 24
const MONTH = DAY * 30
const YEAR = MONTH * 12
We can also use console.time() and console.timeEnd() method for the same thing.
Syntax:
console.time(label);
console.timeEnd(label);
Label:
The name to give the new timer. This will identify the timer; use the same name when calling console.timeEnd() to stop the timer and get the time output to the console.
let promise = new Promise((resolve, reject) => setTimeout(resolve, 400, 'resolved'));
// Start Timer
console.time('x');
promise.then((result) => {
console.log(result);
// End Timer
console.timeEnd('x');
});
You can simply use performance.now()
Example:
start = performance.now();
elapsedTime = performance.now() - start;
var hours = 0;
if(minutes == 59 && seconds == 59)
{
hours = hours + 1;
minutes = '00';
seconds == '00';
}
I would use the getTime() method, subtract the time and then convert the result into hh:mm:ss.mmm format.
I know this is kindda old question but I'd like to apport my own solution in case anyone would like to have a JS encapsulated plugin for this. Ideally I would have: start, pause, resume, stop, reset methods. Giving the following code all of the mentioned can easily be added.
(function(w){
var timeStart,
timeEnd,
started = false,
startTimer = function (){
this.timeStart = new Date();
this.started = true;
},
getPartial = function (end) {
if (!this.started)
return 0;
else {
if (end) this.started = false;
this.timeEnd = new Date();
return (this.timeEnd - this.timeStart) / 1000;
}
},
stopTime = function () {
if (!this.started)
return 0;
else {
return this.getPartial(true);
}
},
restartTimer = function(){
this.timeStart = new Date();
};
w.Timer = {
start : startTimer,
getPartial : getPartial,
stopTime : stopTime,
restart : restartTimer
};
})(this);
Start
Partial
Stop
Restart
What I found useful is a 'port' of a C++ construct (albeit often in C++ I left show implicitly called by destructor):
var trace = console.log
function elapsed(op) {
this.op = op
this.t0 = Date.now()
}
elapsed.prototype.show = function() {
trace.apply(null, [this.op, 'msec', Date.now() - this.t0, ':'].concat(Array.from(arguments)))
}
to be used - for instance:
function debug_counters() {
const e = new elapsed('debug_counters')
const to_show = visibleProducts().length
e.show('to_show', to_show)
}

Showing milliseconds in a timer html [duplicate]

This question already has answers here:
Adding milliseconds to timer in html
(2 answers)
Closed 8 years ago.
How do i show a countdown timer in html?
Current code:
var count=6000;
var counter=setInterval(timer, 1);; //1000 will run it every 1 second
function timer(){
count=count-1;
if (count <= 0){
clearInterval(counter);
return;
}
document.getElementById("timer").innerHTML=count + " milliseconds"; // watch for spelling
}
Converting seconds to ms
function msToTime(s) {
var ms = s % 1000;
s = (s - ms) / 1000;
var secs = s % 60;
s = (s - secs) / 60;
var mins = s % 60;
var hrs = (s - mins) / 60;
return hrs + ':' + mins + ':' + secs + '.' + ms;
}
How would i call out the timer?
still shows the timer as ms. i want it to show up as 99:00 seconds instead of 9900 milliseconds.
Thanks
You can do something like this:
var expires = new Date();
expires.setSeconds(expires.getSeconds() + 60); // set timer to 60 seconds
var counter = setInterval(timer, 1);
function timer() {
var timeDiff = expires - new Date();
if (timeDiff <= 0) {
clearInterval(counter);
document.getElementById("timer").innerHTML = "00:00";
return;
}
var seconds = new Date(timeDiff).getSeconds();
var milliSeconds = (new Date(timeDiff).getMilliseconds() / 10).toFixed(0);
var seconds = seconds < 10 ? "0" + seconds : seconds;
var milliSeconds = milliSeconds < 10 ? "0" + milliSeconds : milliSeconds;
document.getElementById("timer").innerHTML = seconds + ":" + milliSeconds; // watch for spelling
}
Here i set the start time from the javascript Date() function and then calculate the difference from current time in the timer() function.
Check it out here: JSFiddle
If it's JavaScript and has to do with Time I use moment.js
http://momentjs.com
Moment defaults to milliseconds as it's first parameter:
moment(9900).format("mm:ss"); Is 9 seconds, displayed as: 00:09
http://plnkr.co/edit/W2GixF?p=preview
Here's an actually accurate timer (in that it actually shows the correct amount of time left). setInterval will never call every 1 ms regardless of what you ask for because the actually resolution isn't that high. Nor can you rely on consistency because it's not running in a real-time environment. If you want to track time, compare Date objects:
var count=60000;
var start = new Date();
var counter=setInterval(timer, 1); //1000 will run it every 1 second
function timer(){
var left = count - (new Date() - start);
if (left <= 0){
clearInterval(counter);
document.getElementById("timer").innerHTML = msToTime(0) + " seconds";
return;
}
document.getElementById("timer").innerHTML = msToTime(left) + " seconds"; // watch for spelling
}
function msToTime(s) {
var ms = s % 1000;
s = (s - ms) / 1000;
return s + ':' + pad(ms, 3);
}
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
<div id='timer'></div>
Borrowing from #Cheery's fiddle as a starting point.

Objectify Javascript - How To Run A Javascript Function multiple Times

I have written a simple Javascript function (curteousy of codecall.net) that creates a count down timer.
It works fine when I run it, but I want to have more than one timer on the page.
When I place the function inside another div I get the numbers on the screen but only one of the last function actually counts down.
I have placed a link to the code here in JsFiddle which for one reason or another doesn't want to run it but it works. I just need multiple instances of it.
Any help is much appreciated, thanks in advance
The way you built it, all in the global namespace, makes it very difficult to incorporate two timers. Instead, you should just use a reusable object constructor. Demo here.
function Countdown(element, time) {
this.element = element;
this.time = time;
}
Countdown.time = function() {
return new Date().getTime() / 1000;
};
Countdown.formatRemaining = function(timeRemaining) {
function fillZero(n) {
return n < 10 ? '0' + n : n.toString();
}
var days = timeRemaining / 60 / 60 / 24 | 0;
var hours = timeRemaining / 60 / 60 | 0;
var minutes = timeRemaining / 60 | 0;
var seconds = timeRemaining | 0;
hours %= 24;
minutes %= 60;
seconds %= 60;
return days + ' day' + (days === 1 ? '' : 's') + ' ' + fillZero(hours) + ':' + fillZero(minutes) + ':' + fillZero(seconds);
};
Countdown.prototype.update = function() {
var timeRemaining = this.time + this.start - Countdown.time();
if(timeRemaining > 0) {
this.element.innerHTML = Countdown.formatRemaining(timeRemaining);
} else {
this.element.innerHTML = "Time's up!";
if(this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
};
Countdown.prototype.start = function() {
var countdown = this;
this.start = Countdown.time();
this.timer = setInterval(function() {
countdown.update();
}, 1000);
this.update();
};

Categories