Countdown Timer Objects - Javascript - javascript

I would like to create a simple timer in Javascript that counts down from a given time until it hits 0. I found this tutorial which worked perfectly. My problem is that I need to place multiple timers on the same page. This tutorial obviously won't do that because it uses global variables (I'm new to JS/Programming so I might not be using the right terms). I tried to re-create the same thing only creating each timer as it's own Object so that they don't interfere with eachother. This is what I have.
function taskTimer(name, startTime) {
this.timer = name;
this.totalSeconds = startTime;
this.tick = function() {
if (this.totalSeconds <= 0) {
return;
}
this.totalSeconds -= 1;
this.updateTimer();
// window.setTimeout("this.tick()", 1000);
};
this.updateTimer = function(){
this.seconds = this.totalSeconds;
this.hours = Math.floor(this.seconds / 3600);
this.seconds -= this.hours * (3600);
this.minutes = Math.floor(this.seconds / 60);
this.seconds -= this.minutes * (60);
this.timeString = this.leadingZero(this.hours) + ":" + this.leadingZero(this.minutes) + ":" + this.leadingZero(this.seconds);
return this.timeString;
};
this.leadingZero = function(time){
return (time < 10) ? "0" + time : + time;
};
}
var testTimer = new taskTimer("timer", 30);
testTimer.tick();
I created one at the end there. Running
testTimer.updateTimer(); returns 00:00:30 which is correct, but running testTimer.tick(); returns no value. There is obviously something wrong with that part of the code I just can't figure it out.

You've got a few problems.
You're calling updateTimer() inside of your tick method, so it
won't ever reach outside of there unless you return it.
With your current setup, you'd have to call tick manually every time you wanted to update the clock, and if you don't do that precisely every one second the timer will be inaccurate.
To go with #2, you shouldn't decrement totalSeconds like you are because it isn't guaranteed that it will be exactly one second between triggers of your timeout. Use dates instead.
Here's what I would do: http://jsfiddle.net/R4hnE/3/
// I added optional callbacks. This could be setup better, but the details of that are negligible.
function TaskTimer(name, durationInSeconds, onEnd, onTick) {
var endTime,
self = this, // store a reference to this since the context of window.setTimeout is always window
running = false;
this.name = name;
this.totalSeconds = durationInSeconds;
var go = (function tick() {
var now = new Date().getTime();
if (now >= endTime) {
if (typeof onEnd === "function") onEnd.call(self);
return;
}
self.totalSeconds = Math.round((endTime - now) / 1000); // update totalSeconds placeholder
if (typeof onTick === "function") onTick.call(self);
window.setTimeout(tick, 1000 / 12); // you can increase the denominator for greater accuracy.
});
// this is an instance method to start the timer
this.start = function() {
if (running) return; // prevent multiple calls to start
running = true;
endTime = new Date().getTime() + durationInSeconds * 1000; // this is when the timer should be done (with current functionality. If you want the ability to pause the timer, the logic would need to be updated)
go();
};
}
// no reason to make this an instance method :)
TaskTimer.prototype.toTimeString = function() {
var hrs = Math.floor(this.totalSeconds / 60 / 60),
min = Math.floor(this.totalSeconds / 60 - hrs * 60),
sec = this.totalSeconds % 60;
return [hrs.padLeft("0", 2), min.padLeft("0", 2), sec.padLeft("0", 2)].join(" : ");
};
var task = new TaskTimer("task1", 30, function() {
document.body.innerHTML = this.toTimeString();
alert('done');
}, function() {
document.body.innerHTML = this.toTimeString();
});

I always have problems with this and in one instance, within the function, I had to redefine it:
this.tick = function() {
self=this;
if (self.totalSeconds <= 0) {
return;
}
self.totalSeconds -= 1;
self.updateTimer();
// window.setTimeout("self.tick()", 1000);
};
Here is another post about that: var self = this?

My version, you can see at:
http://fiddle.jshell.net/hmariod/N7haK/4/
var tmArray = new Array();
var timerRef, timerId=0;
function newTimer(){
try{
if(tmArray.length > 4) throw "Too much timers";
var countDown = parseInt(document.getElementById("tCountown").value,10);
if(isNaN(countDown)) throw "tCountown is NaN";
var tmName = document.getElementById("tName").value;
var nt = new taskTimer(++timerId, tmName, countDown);
createTmElement(timerId, tmName);
tmArray.push(nt);
if(!timerRef) timerRef = setInterval(timerFn, 1000);
showTimers();
}catch(er){
alert("newTimer:" + er);
}
}
function taskTimer(id, name, tCountown) {
this.id = id;
this.tName = name;
this.tCountown = tCountown;
}
function timerFn(){
try{
var i;
killTimer = true;
for(i = 0; i < tmArray.length; i++){
tmArray[i].tCountown--;
if(tmArray[i].tCountown < 0){
tmArray[i].tCountown = 0;
}else{
killTimer = false;
}
}
if(killTimer) clearInterval(timerRef);
showTimers();
}catch(er){
clearInterval(timerRef);
aler("timerFn: " + er);
}
}

Related

how to avoid call to postmessage

In my application I have Count down functionality which uses setInterval as follows :
this.timer = window.setInterval(() => {
const time_delta = (this.target_date - new Date() ) / 1000
if (time_delta > 0) {
this.days = time_delta / (24 * 3600)
this.hour = time_delta % (24 * 3600) / (3600)
this.minute = (time_delta % (24 * 3600) % (3600)) / 60
this.second = time_delta % 60
} else {
this.days = 0
this.hour = 0
this.minute = 0
this.second = 0
}
if(time_delta <= 1 && time_delta > 0 ){
clearInterval(this.timer);
location.reload();
}
},10000)
Now there is another functionality which has completely different functionality and it inherits postmessage
(function (window, undefined) {
function MessageChannel(group, targetWindow, origin) {
this._msgNo = 1;
this._group = group;
this._origin = '*' || origin;
this._requestHandler = {};
this._messageQueue = {};
this._targetWindow = targetWindow || window.parent;
window.addEventListener('message', receiveMessage.bind(this), false);
}
MessageChannel.prototype.subscribeRequestMessage = function (msg, callback) {
// Some logic
};
MessageChannel.prototype.unsubscribeRequestMessage = function (msg, callback) {
// Some logic
};
MessageChannel.prototype.postMessage = function (msg, data, timeout) {
// Some logic
};
function receiveMessage(event) {
// Some logic
}
window.Granite = window.Granite || {};
window.Granite.author = window.Granite.author || {};
window.Granite.author.MessageChannel = MessageChannel;
}(this));
The problem I have is that whenever the setInterval executes in every seconds in the Count down timer then the MessageChannel.prototype.postMessage function also executes.
The Count down timer is a component present within an iFrame of a html page and this page also have the code of MessageChannel.prototype.postMessage and its available globally by the framework/tool I am using to build my page. Whenever there is a change in the component then MessageChannel.prototype.postMessage is triggered by the framework. In this case the timer value changes which triggers postMessage call.
I do not want MessageChannel.prototype.postMessage code to execute. How can I avoid this?

How does one refactor best this timer/stopwatch code-base towards a better code-reuse following the OOP paradigm and the DRY principle?

I am having trouble with following OOP in javascript.
How can I make this code more Object-oriented and reusable?
I tried reading up on OOP concepts in JS but couldn't figure a way around making this code be one. Any suggestions?
PS: This is the code for making a stopwatch
//Define variables to hold time values
let seconds = 0;
let minutes = 0;
let hours = 0;
//Define variable to hold "display" value
let interval = null;
//Define variable to hold the clock status
let status = "paused";
//Clock function ( logic to determine when to increment next value, etc.)
function clock() {
seconds++;
//Logic to determine when to increment next value
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
}
}
//Display updated time values to user
document.getElementById("display").innerHTML =
//If seconds/minutes/hours are only one digit, add a leading 0 to the value
`${hours ? (hours > 9 ? hours : `0${hours}`) : "00"}:${minutes ? (minutes > 9 ? minutes : `0${minutes}`) : "00"}:${seconds > 9 ? seconds : `0${seconds}`}`;
}
function startPause() {
if (status === "paused") {
//Start the stopwatch (by calling the setInterval() function)
interval = window.setInterval(clock, 1000);
document.getElementById("startPause").innerHTML = "Pause";
status = "started";
} else {
window.clearInterval(interval);
document.getElementById("startPause").innerHTML = "Resume";
status = "paused";
}
}
//Function to reset the stopwatch
function reset() {
seconds = 0;
minutes = 0;
hours = 0;
document.getElementById("display").innerHTML = "00:00:00";
document.getElementById("startPause").innerHTML = "Start";
window.clearInterval(interval);
status = "paused";
}
One could use kind of a "component based" approach. The provided example code is pretty straightforward. The constructor does assign all the needed references as public properties to a Stopwatch instance. Getters and setters for reading and writing the component's UI(/DOM) part are implemented as prototypal methods. Helper methods (interval handling, time measure computations) do not need to be part of the class implementation itself, but will be residents of the Stopwatch module scope ...
// module scope of e.g. 'Stopwatch.js' file.
const MEASURE_STATE_RUNNING = 'running';
const MEASURE_STATE_STOPPED = 'stopped';
const UPDATE_CYCLE_MINIMUM = 100; // any value in msec.
const UPDATE_CYCLE_DEFAULT = 200; //
const UPDATE_CYCLE_MAXIMUM = 1000; //
function getDisplayNumber(value) {
return ((String(value).length === 1) && `0${ value }`) || value;
}
function getMeasureInMilliseconds(measure) {
return (((measure.hours * 3600) + (measure.minutes * 60) + measure.seconds) * 1000);
}
function getMeasureFromMilliseconds(value) {
let hours = (value / 3600000);
let minutes = ((hours - Math.floor(hours)) * 60);
let seconds = ((minutes - Math.floor(minutes)) * 60);
hours = Math.floor(hours);
minutes = Math.floor(minutes);
seconds = Math.floor(seconds + 0.001);
seconds = ((seconds < 60) && seconds) || 0;
minutes = ((minutes < 60) && minutes) || 0;
hours = ((hours < 100) && hours) || 0;
return { hours, minutes, seconds };
}
function handleStartStopForBoundStopwatch(/* evt */) {
const stopwatch = this;
if (stopwatch.measureState === MEASURE_STATE_STOPPED) {
stopwatch.startMeasure();
} else {
stopwatch.stopMeasure();
}
}
function updateStopwatchMeasure(stopwatch) {
const dateNow = Date.now();
// has at least one second past since the last measure update?
const isUpdateMeasure = (Math.floor((dateNow - stopwatch.updateTimestamp) / 1000) >= 1);
if (isUpdateMeasure) {
stopwatch.updateTimestamp = dateNow;
// time differences in milliseconds since measuring has been started the last time.
const timePassed = (dateNow - stopwatch.measureTimestamp);
const messureValue = (timePassed + stopwatch.lastMeasuredMSecs);
Object.assign(stopwatch.measure, getMeasureFromMilliseconds(messureValue));
stopwatch.setComponentMeasure();
}
}
class Stopwatch {
constructor(node) {
this.node = node;
this.timerId = null;
this.updateCycle = this.getComponentUpdateCycle();
// for synchronizing display values of a running measure.
this.lastMeasuredMSecs = null;
this.measureTimestamp = null;
this.updateTimestamp = null;
this.measure = this.getComponentMeasure();
this.measureState = this.getComponentMeasureState();
// synchronize component data initially.
this.setComponentMeasure();
this.setComponentMeasureState();
if (this.measureState === MEASURE_STATE_RUNNING) {
this.startMeasure();
}
this.startStopHandler = handleStartStopForBoundStopwatch.bind(this);
node.addEventListener('click', this.startStopHandler);
}
destroy() {
if (this.node) {
this.node.removeEventListener('click', this.startStopHandler);
this.node.remove();
this.node = null;
delete this.node;
}
this.timerId = this.updateCycle = this.lastMeasuredMSecs = null;
this.measureTimestamp = this.updateTimestamp = null;
this.measure = this.measureState = this.startStopHandler = null;
delete this.timerId;
delete this.updateCycle;
delete this.lastMeasuredMSecs;
delete this.measureTimestamp;
delete this.updateTimestamp;
delete this.measure;
delete this.measureState;
delete this.startStopHandler;
}
getComponentMeasure() {
const result =
(/^(?<hours>\d{1,2})\:(?<minutes>\d{1,2})\:(?<seconds>\d{1,2})$/)
.exec(
this.node.dateTime
);
const {
hours,
minutes,
seconds
} = (result && result.groups) || { hours: 0, minutes: 0, seconds: 0 };
return {
hours: parseInt(hours, 10),
minutes: parseInt(minutes, 10),
seconds: parseInt(seconds, 10)
};
}
setComponentMeasure() {
const { hours, minutes, seconds } = this.measure;
const value = [
getDisplayNumber(hours),
getDisplayNumber(minutes),
getDisplayNumber(seconds)
].join(':');
this.node.dateTime = value;
this.node.innerText = value;
}
getComponentMeasureState() {
return (
((this.node.dataset.measureState || '').trim() === 'running')
&& MEASURE_STATE_RUNNING
|| MEASURE_STATE_STOPPED
);
}
setComponentMeasureState() {
this.node.dataset.measureState = this.measureState;
if (this.measureState === MEASURE_STATE_RUNNING) {
this.node.classList.add(MEASURE_STATE_RUNNING);
this.node.classList.remove(MEASURE_STATE_STOPPED);
} else {
this.node.classList.add(MEASURE_STATE_STOPPED);
this.node.classList.remove(MEASURE_STATE_RUNNING);
}
}
getComponentUpdateCycle() {
let value = parseInt(this.node.dataset.updateCycle, 10);
value = (Number.isNaN(value) && UPDATE_CYCLE_DEFAULT) || value;
return Math.max(UPDATE_CYCLE_MINIMUM, Math.min(UPDATE_CYCLE_MAXIMUM, value));
}
startMeasure() {
this.measureTimestamp = this.updateTimestamp = Date.now();
this.lastMeasuredMSecs = getMeasureInMilliseconds(this.measure);
this.timerId = setInterval(
updateStopwatchMeasure,
this.updateCycle,
this
);
this.measureState = MEASURE_STATE_RUNNING;
this.setComponentMeasureState();
}
stopMeasure() {
clearInterval(this.timerId);
this.lastMeasuredMSecs = null;
this.measureTimestamp = null;
this.updateTimestamp = null;
this.measureState = MEASURE_STATE_STOPPED;
this.setComponentMeasureState();
}/*
resetMeasure() {
Object.assign(this.measure, {
hours: 0,
minutes: 0,
seconds: 0
});
this.setComponentMeasure();
}*/
static initialize(node) {
return new Stopwatch(node);
}
}
/*export default*/function initialize() {
return Array
.from(document.body.querySelectorAll('.stopwatch-component'))
.map(Stopwatch.initialize);
}
/**
* usage
*/
const timerList = initialize();
// console.log('timerList :', timerList);
dd {
margin-bottom: 6px;
}
.stopwatch-component {
font-family: monospace;
font-size: x-large;
cursor: pointer;
}
.stopwatch-component.stopped {
text-decoration: line-through solid #999;
}
<dl>
<dt>
1st time measurement
</dt>
<dd>
<time
class="stopwatch-component"
datetime="0:0:0"
data-update-cycle="100"
data-measure-state="running">0:0:0</time>
</dd>
<dt>
2nd time measurement
</dt>
<dd>
<time
class="stopwatch-component"
datetime="99:59:33"
data-update-cycle="1000">99:59:33</time>
</dd>
<dt>
3rd time measurement
</dt>
<dd>
<time
class="stopwatch-component"
datetime="07:11:55"
data-update-cycle="500"
data-measure-state="stopped">7:11:55</time>
</dd>
</dl>
<p>Click each time measure separately for toggling its pause/proceed state.</p>
Note:
In order to keep the (re)rendering of seconds (minutes, hours) that have been passed in sync, one needs another timer approach than the one provided by the OP.
The examples run with different configurable update cycles defined by the value of the data-update-cycle attribute of a <time class="stopwatch-component"/> element. An update interval of 500msec or just 1sec is not suitable for this kind of measure task, due to setInterval being not precise enough. The running example does demonstrate exactly that.
One has to keep the displayed data in sync by constantly comparing the timestamp from starting a measure process with the timestamp from the current update process. The (re)rendering of a displayed value of cause takes place only if at least a second has passed since the last (re)rendering. But the update cycles which constantly (but not permanently) triggers the (re)rendering has to be run at a higher frequency.

HTML 5 Audio Player remaining time show delay

I have a problem with an audio player, when users press play the start-time timer is showing instantly, but remaining-time timer shows with a delay. I'm relatively new to JS, so I can not identify the problem on my own.
Can someone can help me with synching timers (start and remaining) show on play event?
CodePen
var isSeeking = false;
var seek = document.getElementById("seekObj");
var player = document.getElementById("player");
SetSeekColor();
function calculateTotalValue(length) {
var minutes = Math.floor(length / 60),
seconds_int = length - minutes * 60,
seconds_str = seconds_int.toString(),
seconds = seconds_str.split(".")[0],
temp_min = minutes.toString().length === 1 ? "0" + minutes : minutes,
temp_sec = seconds.toString().length === 1 ? "0" + seconds : seconds;
return temp_min + ":" + temp_sec;
}
function calculateCurrentValue(_seconds) {
function padTime(t) {
return t < 10 ? "0" + t : t;
}
if (typeof _seconds !== "number") return "";
if (_seconds < 0) {
_seconds = Math.abs(_seconds);
//console.log(_seconds);
}
var hours = Math.floor(_seconds / 3600),
minutes = Math.floor((_seconds % 3600) / 60),
seconds = Math.floor(_seconds % 60);
var hour = hours > 0 ? padTime(hours) + ":" : "";
return hour + padTime(minutes) + ":" + padTime(seconds);
}
function setupSeek() {
seek.max = player.duration;
}
function seekAudio() {
isSeeking = true;
player.currentTime = seek.value;
isSeeking = false;
}
var prevcurrentime = 0;
function initProgressBar() {
if (!isSeeking) {
seek.value = player.currentTime;
}
var length = player.duration;
var current_time = player.currentTime;
// calculate total length of value
var totalLength = calculateTotalValue(length);
// calculate current value time
var currentTime = calculateCurrentValue(current_time);
if (player.readyState === 4) {
jQuery(".end-time").html(totalLength);
jQuery(".start-time").html(currentTime);
}
//checking if the current time is bigger than the previous or else there will be sync different between remaining and current
if (currentTime > prevcurrentime) {
//calculate the remaining time
var rem_time = length - current_time;
jQuery(".rem-time").html(calculateCurrentValue(rem_time));
}
//setting the previouscurrent time to this current time
prevcurrentime = currentTime;
if (player.currentTime == player.duration) {
$("#play-btn").removeClass("pause");
}
}
function initPlayers(num) {
// pass num in if there are multiple audio players e.g 'player' + i
for (var i = 0; i < num; i++) {
(function() {
// Variables
// ----------------------------------------------------------
// audio embed object
var playerContainer = document.getElementById("player-container"),
player = document.getElementById("player"),
isPlaying = false,
playBtn = document.getElementById("play-btn");
// Controls Listeners
// ----------------------------------------------------------
if (playBtn != null) {
playBtn.addEventListener("click", function() {
togglePlay();
});
}
// Controls & Sounds Methods
// ----------------------------------------------------------
function togglePlay() {
if (player.paused === false) {
player.pause();
isPlaying = false;
$("#play-btn").removeClass("pause");
} else {
$(".start-time").html("");
player.play();
$("#play-btn").addClass("pause");
isPlaying = true;
}
}
})();
}
}
looking good, this is an error in the html. In this piece of code just replace the class rem-time by end-time.
<small style="float: right; position: relative; right: 21px;" class="end-time" onclick="showhideRemaining(this)"></small>
I think the root of your problem lies within the performance of your code. I can't see a big time difference between the two counters, but here are some suggested improvements:
You have the padTime function, so use it (see point 2)!
function padTime(t) {
return t < 10 ? "0" + t : t;
}
no unnecessary or early conversion to strings:
function calculateTotalValue(length) {
var minutes = Math.floor(length / 60);
var seconds = Math.floor(length - minutes * 60);
// use the padTime function here with numbers
return padTime(minutes) + ":" + padTime(seconds);
}
do not query the elements over and over again:
if (player.readyState === 4) {
jQuery(".end-time").html(totalLength);
jQuery(".start-time").html(currentTime);
}
Here, instead of querying the elements (jQuery(".end-time")) every time in your ontimeupdate callback, save the references in variables outside of the callback function.
limit color updates.
setInterval(function() {
SetSeekColor();
}, 34);
Updating the color every millisecond is just overkill. 34 milliseconds should equal something around 30fps.
I noticed you have are planning to have multiple players on one page (initPlayers(num)). Here a few thoughts:
When you initialise the players, save each single player with its UI elements in an Object
initPlayers(jQuery("#player-container").length);: IDs are (must be) unique in an entire HTML Document. This wont work, change it to a class.

How to pause and resume timer object

I am making a timer, well I am making timers...basically you can start one timer for something but then the idea is that in the middle of that you might want to start something else so you can pause it and start another one and then when that is done, you can resume the initial timer.
The timer object is working fine and I can get multiple timers going at the same time...my problem is that when i go to resume the first timer it waits for the amount of time that the other timer has been going before resuming.
I have a basic fiddle of it here: https://jsfiddle.net/1ea07uv9/19/
My apologies for the code...I have been messing around with it trying to get it to work it has become quite the mess...it doesnt work exactly the way I have it in my local environment but it illustrates my problem....in the fiddle if you click begin first timer...wait about ten seconds or so and then click on timer one it stops...for about 10 seconds and then continues...I need it to just continue straight away.
Im pretty sure that it has to do with the lines:
var diff = (new Date().getTime() - currentObj.start) - currentObj.time;
currentObj.timerInit = window.setTimeout(instance, (1000 - diff));
where currentObj.start is now however long you waited behind?
EDIT: It was requested I post my code in this post instead of just a fiddle so here it is
//Array where jobs will be stored
var jobs = [],
begin = false;
//Job object
function Job(name, active, s, m, h, elapsed){
var currentObj = this;
this.name = name;
this.seconds = s;
this.minutes = m;
this.hours = h;
this.time = 0;
this.elapsed = elapsed;
this.timeLimit = 28800;
this.completePercent = 0;
this.jobNumber = 0;
this.timer = function(begin){
var secondStr = '00',
minuteStr = '00',
hourStr = '00';
currentObj.start = new Date().getTime();
function instance(){
currentObj.time += 1000;
currentObj.elapsed = Math.floor(currentObj.time / 1000);
secondStr = currentObj.seconds.toString();
if(secondStr.length == 1){
secondStr = '0' + currentObj.seconds.toString();
}
minuteStr = currentObj.minutes.toString();
if(minuteStr.length == 1){
minuteStr = '0' + currentObj.minutes.toString();
}
hourStr = currentObj.hours.toString();
if(currentObj.seconds <= 58){
currentObj.seconds++;
}else{
currentObj.seconds = 0;
if(currentObj.minutes <= 58){
currentObj.minutes++;
}else{
currentObj.minutes = 0;
currentObj.hours++;
}
}
$('[data-job-id="'+currentObj.jobNumber+'"]').html(hourStr + ':' + minuteStr + ':' + secondStr);
var diff = (new Date().getTime() - currentObj.start) - currentObj.time;
currentObj.timerInit = window.setTimeout(instance, (1000 - diff));
}
if(begin){
currentObj.timerInit = window.setTimeout(instance, 1000);
}
}
this.pause = function(){
clearTimeout(this.timerInit);
}
}
//Create the snooze timer object and chuck it into the array
jobs.push(new Job('snooze', false, 0, 0, 0, 0));
//Begin specific timer
$('#timer_start').click(function(e){
e.preventDefault();
if(jobs.length <= 3){
//Create new job object and push it into the array
jobs.push(new Job('jobNameInput', true, 0, 0, 0, 0));
//Loop through all of the jobs and give it a number (according to its array position)
for(var i=0; i < jobs.length; i++){
jobs[i].jobNumber = i;
//Give the jobs their names but check we arent changing the snooze button!
if(i == jobs.length - 1){
jobs[i].timer(true);
}
}
}else{
alert('sorry mate...you can only have 3 jobs!');
}
});
//Switch timer
$('.start').click(function(){
var thisTimer = $(this).next('.time').attr('data-job-id');
for(var i=0; i < jobs.length; i++){
jobs[i].pause();
}
jobs[thisTimer].timer(true);
});

Trying to get timer to start once arrived on the page

I made the following fiddle of what I have right now..
https://jsfiddle.net/r5yj99bs/1/
I'm trying to start right when I get onto a page, but allowing the option to leave the pause/resume option. Then is there anyway to display the remaining time as '5 minutes' instead of '300 seconds' and then count down that way rather than only seconds.
<button class="start-pause">Start</button>
<h2 class="time-left"></h2>
var times = [];
var counter_interval;
var $time_left = $('.time-left');
var $button = $('.start-pause');
// timer length in seconds
var timer_length = 300;
$('body').on('click', '.start-pause', function() {
// are we starting or stopping?
var starting = times.length % 2 == 0;
times.push(Date.now());
if (starting) {
$button.html('Pause');
counter_interval = setInterval(function() {
var time_left = Math.floor(timer_length - sum_elapsed());
if (time_left < 1) {
clearInterval(counter_interval);
return finished();
}
$time_left.html(time_left);
}, 100);
} else {
$button.html('Resume');
clearInterval(counter_interval);
}
});
var sum_elapsed = function() {
sum = 0;
for (var i=0; i<times.length; i++) {
if (i % 2 == 1) {
sum += (times[i] - times[i-1]);
}
if (i == (times.length - 1)) {
sum += (Date.now() - times[i]);
}
}
// convert milliseconds to seconds
return sum / 1000;
};
var finished = function() {
$button.attr('disabled','disabled').html('Finished');
$time_left.html("Time's Up");
};
There is a good time module called moment. You can get it through npm or from moments.com
That can format relative time to human readable strings.
If you want to do it yourself, take the seconds modulus 60 to get the minutes. Using modulus you can extract all info about hours and so on.
You may change the following line:
$time_left.html(time_left);
to:
$time_left.html(secToMinTxt(time_left));
and add the following functions:
function pad(num) {
var str = "" + num;
var pad = "00";
return pad.substring(0, pad.length - str.length) + str;
}
function secToMinTxt(seconds) {
var min = Math.floor(seconds / 60);
var sec = seconds % 60;
return pad(min) + ":" + pad(sec);
}
JSFiddle reference : https://jsfiddle.net/r5yj99bs/2/
If interpret Question correctly, try using Math.round with argument existing time_left variable divided by 60
var time_left = Math.round(Math.floor(timer_length - sum_elapsed()) / 60);
jsfiddle https://jsfiddle.net/r5yj99bs/3/

Categories

Resources