Alternative to setInterval() - Keep time while computer is locked/doing other stuff in Chrome - javascript

I have a timer to keep track of the time the user spends doing something. There's a button that, among other things, resets the timer to zero. That works fine.
However, when the user is away (i.e. taking their 15 or 30 minute breaks), the computer must be locked. I've noticed that the time is inconsistent upon return. Some times it appears the timer doesn't move until the user returns. Sometimes it's a few seconds, or a few minutes, but is never the time the user was way - always LESS.
For instance, if I lock the PC now, and the timer is on 5 minutes and 38 seconds, and I take a 15 minute break, I'll typically return to a timer that's roughly where it was when I left, or perhaps it'll be like 6 minutes and 5 seconds.
How can I fix this? Is there any way to ensure setInterval() continues to behave consistently, or am I better doing something else (such as comparing time stamps instead of an independent counter like I have now)?
For what it's worth, it seems even MORE inconsistent now that we are using VDI workstations, though I don't know if that makes a difference (the issue happened on normal workstations as well).
EDIT: I noticed in another question this appears to also be a thing with Chrome (maybe)? And how it throttles when not the active window or tab. Are there workarounds for --app-mode windows?

If you set the start time as a variable, then you can calculate the the current time of the counter in the interval by subtracting the timestamp that you stored as a variable from the current time. So, then even if the interval pauses while the computer is locked, then when you unlock it and the interval resumes, it will calculate the correct time.
You could also use window.sessionStorage to store the start time. I have a timer I created in CodePen that uses session storage to store the values. You can see that here: https://codepen.io/steebn/pen/eYWrPeM
In the snippet below, I modified the code from that codepen to use a global variable instead of session storage since apparently you cannot use session storage in a snippet.
Hopefully this helps.
const qs = (selector) => document.querySelector(selector);
const qsa = (selector) => document.querySelectorAll(selector);
//variable to store the timestamps
const tLog = {
startTime: null,
stopTime: null,
duration: null
};
//variable to store the interval
let timerInterval;
//Add event listeners to each button
qsa(".timer").forEach((btn) => {
btn.addEventListener("click", (event) => {
qsa(`button.timer`).forEach((btn) => (btn.disabled = true));
qs("#time").classList.remove(`paused`);
switch (event.target.id) {
case `start`:
setMessage(`Timer Counting`);
qsa(`button.started`).forEach((btn) => (btn.disabled = false));
tLog.startTime = tLog.duration ?
new Date().getTime() - tLog.duration :
new Date().getTime();
//Start the timer interval
timerInterval = window.setInterval(() => {
const currentTime = new Date().getTime();
//Calculate duration using the starting timestamp in the tLog variable
qs("#time").innerText = getDuration(currentTime - tLog.startTime);
}, 1000);
break;
case `stop`:
setMessage(`Timer Complete`);
qsa(`button.stopped`).forEach((btn) => (btn.disabled = false));
window.clearInterval(timerInterval); //Clear the interval
tLog.stopTime = new Date().getTime();
tLog.duration = tLog.stopTime - tLog.startTime;
break;
case `pause`:
qs(`#time`).classList.add(`paused`);
setMessage(`Timer Paused`);
qsa(`button.paused`).forEach((btn) => (btn.disabled = false));
window.clearInterval(timerInterval);
tLog.stopTime = new Date().getTime();
tLog.duration = tLog.stopTime - tLog.startTime;
break;
case `reset`:
qsa(`button.cleared`).forEach((btn) => (btn.disabled = false));
qs("#time").innerText = `00:00:00`;
qs("#time").classList.remove(`paused`);
Object.keys(tLog).map((key) => (tLog.key = null));
setMessage(`Timer is ready to begin`);
break;
}
});
});
const setMessage = (msg) => (qs(".status").innerText = msg);
const getDuration = (timeStamp) => {
const d = new Date(timeStamp);
const H = d.getUTCHours().toString().padStart(2, 0);
const M = d.getUTCMinutes().toString().padStart(2, 0);
const S = d.getUTCSeconds().toString().padStart(2, 0);
const s = d.getUTCSeconds().toString().padStart(3, 0);
return `${H}:${M}:${S}`;
};
.btn {
font-size: 3rem !important;
}
#time {
position: relative;
font-size: 4rem;
}
#time.paused {
animation: animate 1.25s linear infinite;
}
#keyframes animate {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css' integrity='sha512-usVBAd66/NpVNfBge19gws2j6JZinnca12rAe2l+d+QkLU9fiG02O1X8Q6hepIpr/EYKZvKx/I9WsnujJuOmBA==' crossorigin='anonymous' />
<body class="bg-dark text-white">
<header class="container d-flex my-0">
<div class="btn-group btn-group-lg mx-auto" role="group" aria-label="Basic example">
<button id="start" class="btn p-0 timer cleared paused">▶️</button>
<button id="pause" class="btn p-0 timer started" disabled>⏸️</button>
<button id="stop" class="btn p-0 timer started paused" disabled>⏹️</button>
<button id="reset" class="btn p-0 timer stopped" disabled>🔃</button>
</div>
</header>
<main class="container d-flex my-0 flex-column">
<div id="time" class="mx-auto">00:00:00</div>
<div class="mx-auto status">Timer is ready to begin</div>
</main>
</body>

Related

How to add audio to a 10 second countdown in javascript

First time posting on here so go easy.
I am having some problems with a recent project. I am trying to create a countdown as the landing page with audio sounds for each number (street fighter 2 sound effects if anyone is familiar). I have managed to get the countdown to work and it will work but only at the click of a button as this is the only way I could get it to work.
Like I said this is not the desired effect as once the countdown finishes I want it to load the main page. Also in regards to adding the sound to each individual number, I have absolutely no idea where to start!
This is my current JS for it
document.addEventListener('DOMContentLoaded', () => {
const timeLeftDisplay = document.querySelector('#time-left')
const startBtn = document.querySelector('#start-button')
let timeLeft = 10
function countDown (){
setInterval(function(){
if(timeLeft <= 0){
clearInterval(timeLeft = 0)
}
timeLeftDisplay.innerHTML = timeLeft
timeLeft -= 1
}, 1000)
}
startBtn.addEventListener('click', countDown)
} )
This is the current HTML
<script type= "text/javascript" src="assets/javascript/script.js"></script>
<title>Bro-nament</title>
</head>
<body>
<div class="container text-center">
<h1 class="bro-title"> TIME TILL BRO - DOWN</h1>
<h2 id="time-left"> 10 </h2>
<button id="start-button"> <i class="fas fa-fist-raised"> Continue? </i> <i class="fas fa-fist-raised"></i></button>
Current page view
Thanks
In your server, you need to name your audio files with a number for all of them and use the value of time variable to increment and get the url of the file for each every seconde.
Like :
9.mp3
8.mp3
7.mp3
6.mp3
....
Once the counter is to 0, you redirect where the url you want.
let time = 10;
countdown();
function countdown() {
// we upadate number text
document.querySelector('#time-left').textContent = --time;
// if count is equal to 0 (end of counter)
if (time === 0) {
time = 10;
// we redirect to this url
window.location.href = "http://www.google.com";
// we stop the loop
return false;
}
//console.log(time);
// we set the url of audio file for each seconde
const audio = new Audio("https://srv-store5.gofile.io/download/RFgvcw/" + time + ".mp3");
// if you only want one, u dont need the const time
//const audio = new Audio("https://srv-store5.gofile.io/download/RFgvcw/1.mp3");
audio.play();
setTimeout(countdown, 1000);
}
#time-left {
font-size: 150px;
font-weight: bold;
margin: auto 0;
text-align: center;
}
<div id="time-left"></div>

Using a clock for Auto click

I am using the code below to execute an Autoclick for an interval, now I want to add the specific time to start and stop the clicking. It would be great if somebody could help me to do that.
there is a clock in the website which the time is different from the system clock(by 1-2 seconds), so I would prefer to use the website clock as the reference.
I appreciate that.
let timerId = setInterval(() => {
let Quant = “1000”;
let Ptag = “10000”;
document.getElementById("send_order_txtCount").value = Quant;
document.getElementById("send_order_txtPrice").value = Ptag;
$("#send_order_btnSendOrder").click();
console.log('clicked');
}, 1000);
// 1000 is the time span to clicking (Mili seconds)
// 4000 is the total time to stop the code
let stop = 4000;
setTimeout(() => {
clearInterval(timerId);
alert('stop');
}, stop);
I am wondering whether this will work or not.
"Start": "9:46:56",
"Stop": "9:47:00",

javascript setTimeout function doesn't work inside a loop

I'm making a Simon Game and I'm trying to make the button presses have 1 second interval. But It seems that my setTimeout function is not doing its job and all clicks are performed at once without the 1s interval. I tried alerting something outside the loop and it works just fine. Can anyone help me with this?
This is my JavaScript code:
for (var count = 1; count <= 20; count++) {
$("#count").html(count);
seq.push(Math.floor(Math.random() * 4));
seq.forEach(function(press) {
setTimeout(function() {
eval('$("#button' + press + '").click();');
}, 1000);
});
}
and the corresponding html:
<p>count: <span id="count">0</span></p>
<button id="button0" onclick="sound1.play()"></button>
<button id="button1" onclick="sound2.play()"></button>
<button id="button2" onclick="sound3.play()"></button>
<button id="button3" onclick="sound4.play()"></button>
Thank you!
The problem is the way you do setTimeout.
The for loop iterates within a few milliseconds and you basically request all the clicks to run one second later, so they all happen one second later but at the same time.
If you request the first click after one, the second click after two seconds and so forth, you'll get what you want:
seq.forEach(function(press, i) {
setTimeout(function() {
$("#button" + press).click();
}, 1000 * i);
});
Also note that you probably want to restructure your code to not do this twenty times over:
for (var count = 1; count <= 20; count++) {
$("#count").html(count);
seq.push(Math.floor(Math.random() * 4));
}
seq.forEach(function(press, i) {
setTimeout(function() {
$("#button" + press).click();
}, 1000 * i);
});
Your eval function is running after 1 second but all of them are.
What happens:
loop from 1 to 20
add an item to the seq array
loop through the seq array
define the setTimeout to happen in 1 second.
Your code does not sleep while wating for the setTimeout to execute. So all of them are defined on the loop and happen as near as possible to the 1 second requested.
You could make an asynchronous loop, by calling a function repeatedly from within a setTimeout: that way the sequencing and delay will be as desired.
Here is a working snippet with some other ideas:
// First generate the array (only 8 to not annoy SO public):
var seq = Array.from(Array(8), _ => Math.floor(Math.random() * 4));
function replay() {
// Iterate seq asynchronously
(function loop(i) {
if (i >= seq.length) return; // all done
$("#count").text(i+1);
$("#buttons>button").eq(seq[i]).click();
setTimeout(loop.bind(null, i+1), 1000);
})(0);
}
$("#buttons>button").click(function () {
// Play sound here...
playNote([523, 659, 784, 880][$(this).index()], 800);
// Some visual effect:
$(this).addClass("clicked");
setTimeout($(this).removeClass.bind($(this), "clicked"), 800);
});
// Sound support
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
function playNote(frequency, duration) {
// create Oscillator node
var oscillator = audioCtx.createOscillator();
oscillator.type = 'square';
oscillator.frequency.value = frequency; // value in hertz
oscillator.connect(audioCtx.destination);
oscillator.start();
setTimeout(oscillator.stop.bind(oscillator), duration);
}
// Play the sequence on page load
replay();
button {
border: 2px solid silver;
}
button.clicked {
border: 2px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>count (up to 8): <span id="count">0</span></p>
<div id="buttons">
<button>A</button>
<button>B</button>
<button>C</button>
<button>D</button>
</div>

Fast forward timer

I'm having trouble with fast forwarding a timer. It is very basic at this stadium. I have a interval that add numbers. Like this:
setInterval(function () {
//+1 second
//Format output to 00:00
//Handle minute update
}, 1000);
This works perfect. The timer is going at normal speed. What I want to do is fast forwarding this timer. I want a timer minute to take 1 real second. I have tried:
setInterval(function () {
//+1 second
//Format output to 00:00
//Handle minute update
}, 15);
That works sometimes and sometimes not. Sometimes it stops att 01:02 instead of 01:00. It may be my lack of math knowledge but I don't know. How would you do it? I am going to stop and start the timer every "timer minute" so it's important that the interval is correct.
EDIT
Here is a fiddle of how I want it to work: http://jsfiddle.net/tbleckert/pF4gs/
EDIT 2
Maybe I should just adjust the time when I stop the timer?
EDIT 3
It seems like 15 ms works most of the times. But something makes ut unreliable, I think the best way is to just adjust the time.
I think what you should be doing is storing your interval in a variable so that you can clear it, and start it again with a different delay.
var delay = 1000; // JavaScript uses milliseconds, so 1000 = 1 second
var theTimer = '';
function startTimer(){
theTimer = setInterval(function () {
// Do awesome stuff here
}, delay);
}
startTimer();
Now when you want to change the interval, or fast forward the timer, all you have to do is clear the current setInterval and define it again -
clearInterval(theTimer); // stop and clear the current timer
delay = 500; // Crank it up to twice the speed! 0.5 seconds!
startTimer(); // start a new setInterval();
Here is a simple demo
<!DOCTYPE html>
<html>
<head>
<script>
function myTimer(){
this.startTime=0;
this.intervalID=0;
this.timePassed=0;
this.multiplier=1;
this.outputElement=null;
this.start=function(){
clearInterval(this.intervalID);
this.timePassed=0;
this.outputElement=document.getElementById("output");
this.startTime=new Date();
var me = this;
this.intervalID=setInterval(function(){
me.update(me);
},100);
}
this.toTwoDigit=function(num){
if(num<10){
return "0"+num;
}
return new String(num);
}
this.toThreeDigit=function(num){
if(num<10){
return "00"+num;
}
if(num<100){
return "0"+num;
}
return new String(num);
}
this.update=function(me){
me.timePassed=me.timePassed+(100*me.multiplier);
var seconds=Math.floor(me.timePassed/1000);
var hours = Math.floor(seconds/3600);
var minutes = seconds-(hours*3600);
seconds = seconds%60;
minutes=Math.floor(minutes/60);
me.outputElement.innerHTML= me.toTwoDigit(hours)+":"
+me.toTwoDigit(minutes)+":"
+me.toTwoDigit(seconds)
+":"+me.toThreeDigit(Math.floor(me.timePassed%1000));
}
this.speedup=function(){
this.multiplier=this.multiplier*2;
}
this.slowDown=function(){
this.multiplier=this.multiplier/2;
}
this.stop=function(){
clearInterval(this.intervalID);
this.update(this);
}
}
var t = new myTimer();
</script>
</head>
<body onload="t.start();">
<input type="button" value="speed up" onclick="t.speedup()"></input>
<input type="button" value="slow down" onclick="t.slowDown()"></input>
<input type="button" value="stop" onclick="t.stop()"></input>
<input type="button" value="restart" onclick="t.start()"></input>
<input type="button" value="(re)start times 60" onclick="t.multiplier=60;t.start()"></input>
<div id="output"></div>
</body>
</html>
Ok so I'm going to answer this myself. I don't think I was clear enough. When I start the timer a timeout starts at the same time, that after one second stops the timer. This is where it goes wrong, the timer doesn't always show 01:00 when it stops.
So, the final solution is the set the seconds to 00 every time it stops, and because it all happens so fast, you wont notice.
setTimeout(function () {
clearInterval(interval);
$('.clock').html(rMin.slice(-2) + ':00');
}, 1000);
Check my updated fiddle:
http://jsfiddle.net/tbleckert/pF4gs/2/

Fadein at certain hour or time

I wonder if this is possible? For a website concept I want to make something pop out or fade in at a certain hour according to the user's computer time. I have tried looking with no avail because I don't know what kind of function would control when an effect takes place.
Any ideas? Thank you.
If you know at what time to fadeIn (ahem) in, then simply calculate the delta between that time (that's supposedly in the future) and our time. Example:
var dateNow = new Date();
setTimeout(function() {
doFadingInStuff();
}, dateWanted - dateNow);
If you get the current time and calculate the future time that you want the event to happen (thus you have an amount of elapsed time until your event should happen), you can use the setTimeout() function to schedule a function call at a precise time in the future. From that function, you would do your animation.
Check out this fiddle
Your JS
var now = new Date().toString();
$('#fadeMe').html(now).fadeIn(9000)
Assuming you only want something to be visible during a certain hour (ie. fade in when it becomes that hour, fade out when it's no longer that hour), you could do this:
// element is assumed to be a jQuery object
var VISIBLE_HOUR=14; // 2:00 PM
function check() {
var isHour=(new Date()).getUTCHours()==VISIBLE_HOUR;
var isVisible=element.is(":visible");
if(isHour!=isVisible) {
element.fadeToggle(1000);
}
}
// We probably don't want to check very frequently...
// You could make it more advanced by checking
// more frequently closer to the hour in which it would be visible.
setInterval(check, 30000);
var eventHour = 16, // 4:00pm
// get the current time as a Date
now = new Date(),
// turn your event time into a Date
eventDate = new Date(now.getFullYear(),
now.getMonth(),
now.getDate(),
eventHour),
// calculate how many MS until your event
eventTimeMS = eventDate - now;
dayInMS = 86400000,
// your event
myEvent = function() {
alert('The time is now!');
};
// adding 24 hours if already past event time today
eventTimeMS = eventTimeMS < 0 ? eventTimeMS + dayInMS : eventTimeMS;
// if currently the right hour, just invoke event
if (eventHour == now.getHours()) {
myEvent();
// otherwise start a timer to invoke your event at the appropriate time
} else {
setTimeout(myEvent, eventTimeMS);
}
I think you want to check the time of day for the client, then fade in or out. This would be done like so:
<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$(function(){
var sunriseHour = 1;
var sunsetHour = 19;
function checkForChange() {
var nowDate = new Date();
var nowHour = nowDate.getHours();
if ((nowHour >= sunriseHour) && (nowHour < sunsetHour)) {
if (sunPosition != 'up') {
sunPosition = 'up';
$('#theSun').fadeIn('slow');
}
} else {
if (sunPosition != 'down') {
sunPosition = 'down';
$('#theSun').fadeOut('slow');
}
}
}
var nowDate = new Date();
var nowHour = nowDate.getHours();
if ((nowHour >= sunriseHour) && (nowHour < sunsetHour)) {
$('#theSun').show();
sunPosition = 'up';
} else {
$('#theSun').hide();
sunPosition = 'down';
}
setTimeout(checkForChange, 1000);
});
</script>
</head>
<body>
<div id="theSun" style="background: yellow; width: 300px; height: 300px; display: none;">
This box is the sun
</div>
</body>
</html>

Categories

Resources