I can't figure out how to add a restarting timer to my simon game. I know setInterval() and SetTimeout but I can't figure out how to have the timer reset for each level if the level is passed within the first 10 seconds. Do I need an if/else to work and concatenate it to level or userClickPattern? I dont know, I tried it all.
Goal:
Have a 10 second timer that starts .click and if the pattern is completed to move to the next sequence if it is not completed then alert("... ) and restart the game. The timer function is at the bottom. Please help me!!!
var buttonColours = ["red", "blue", "green", "yellow"];
var gamePattern = [];
var userClickedPattern = [];
var started = false;
var level = 0;
$(document).keypress(function() {
if (!started) {
$("#level-title").text("Level " + level);
nextSequence();
started = true;
}
});
$(".btn").click(function() {
var userChosenColour = $(this).attr("id");
userClickedPattern.push(userChosenColour);
playSound(userChosenColour);
animatePress(userChosenColour);
checkAnswer(userClickedPattern.length - 1);
});
function checkAnswer(currentLevel) {
if (gamePattern[currentLevel] === userClickedPattern[currentLevel]) {
console.log("success");
if (userClickedPattern.length === gamePattern.length) {
setTimeout(function() {
nextSequence();
}, 1000);
}
} else {
console.log("wrong");
playSound("wrong");
$("body").addClass("game-over");
setTimeout(function() {
$("body").removeClass("game-over");
}, 200);
$("#level-title").text("Game Over, Press Any Key to Restart");
startOver();
}
}
function nextSequence() {
userClickedPattern = [];
level++;
$("#level-title").text("Level " + level);
var randomNumber = Math.floor(Math.random() * 4);
var randomChosenColour = buttonColours[randomNumber];
gamePattern.push(randomChosenColour);
$("#" + randomChosenColour).fadeIn(100).fadeOut(100).fadeIn(100);
playSound(randomChosenColour);
}
function playSound(name) {
var audio = new Audio("sounds/" + name + ".mp3");
audio.play();
}
function animatePress(currentColor) {
$("#" + currentColor).addClass("pressed");
setTimeout(function() {
$("#" + currentColor).removeClass("pressed");
}, 100);
}
function startOver() {
level = 0;
gamePattern = [];
started = false;
}
function timer() {
alert("Out Of Time Better Luck Next time");
playSound("wrong");
$("#level-title").text("Game Over, Press Any Key to Restart");
startOver();
}
Having a timer is easy with setInterval, which, unlike setTimeout, will continue to execute the handler function with the specified delay until the interval is cleared.
If you wanted the value globally, you could do:
let seconds = 0
let timerInterval
function startTimer() {
timerInterval = setInterval(() => seconds++, 1000)
}
function resetTimer() {
seconds = 0
clearInterval(timerInterval)
}
Related
when i click my button, a timer is supposed to display a countdown timer. But the button does not work.
let timerCounter = document.getElementById("timer-counter");
let timer;
let timerCount;
function startTimer() {
timer = setInterval(function() {
timerCount--;
timerElement.textContent = "Time; " + timerCount;
if (timerCount === 0) {
clearInterval(timer);
}
});
}
startButton.addEventListener("click", startTimer);
This is what I found so far:
You are decrementing the timerCount, need to specify the initial value for it to work.
You're using timerElement instead of timerCounter that you've declared.
You must pass the second args to the setInterval which is delay.
const timerCounter = document.getElementById('timer-counter');
const startButton = document.getElementById('start-button');
let timer;
let timerCount = 30;
startButton.addEventListener('click', startTimer);
function startTimer() {
timer = setInterval(function () {
timerCount--;
timerCounter.textContent = 'Time; ' + timerCount;
if (timerCount === 0) {
clearInterval(timer);
}
}, 1000);
}
<div id="timer-counter"></div>
<button id="start-button">Start</button>
Here's a slightly different approach that avoids some of the problems with global variables. The function the listener calls initialises the count, and then returns a new function (a closure) that is called when the button is clicked. It also uses setTimeout which I find more easy to understand.
// Cache your elements
const counter = document.querySelector('#counter');
const startButton = document.querySelector('button');
// Initialise your count variable
function startTimer(count = 30) {
// Return a function that is called from
// the listener
return function loop () {
// Disabled the button once it's been clicked
if(!startButton.disabled) startButton.disabled = true;
counter.textContent = `Time: ${count}`;
if (count > 0) {
setTimeout(loop, 500, --count);
}
}
loop();
}
// Call startTimer to initialise the count, and return
// a new function that is used as the listener
startButton.addEventListener('click', startTimer(), false);
<div id="counter"></div>
<button>Start</button>
I'm sure this could be improved.
In this example we don't go below 0.
We don't allow timeout collisions ( timeouts don't stack causing weird counting speeds ).
We can reset to the original number when on 0.
const c = document.getElementById('timer-counter')
const b = document.getElementById('start-button')
let timer = false
let timerCount = 30
b.addEventListener('click', start)
function decrement() {
if(timerCount < 0) {
timerCount = 30
timer = false
return
}
c.innerText = `Count: ${timerCount}`
timerCount--
timer = setTimeout(decrement, 200)
}
function start() {
if(timer) return
decrement()
}
<div id="timer-counter"></div>
<button id="start-button">Start</button>
I am working on knockout js.
In that i have a recursive function which executes a function every minute. for that am using a timer every 60 sec it will execute also same will be reflecting in the UI also.
In my case, if i try to assign or initialize a timer value(observable) which is inside a loop, it doesn't reflecting instead of reflecting it is added to the pipeline and that much time loop is running simultaneously.
In that case i want to kill the loop and again want to restart every time i am changing the timer value.
timerInSec=60;
var loop = function () {
if (this.timer() < 1) {
myFunction()
this.timer(this.timerInSec - 1);
setTimeout(loop, 1000);
} else {
this.timer(this.timer() - 1);
setTimeout(loop, 1000);
}
};
loop();
Here is my solution. Please check.
timerInSec = 60;
const Loop = (function () {
let timer = 0;
let timerId = -1;
const myFunction = function () {
console.log('finished');
}
const fnLog = function (tm) {
console.log('current time = ', tm);
}
const fnProc = function () {
timerId = setTimeout(myFunction, 1000 * timer);
}
return {
start: function (tm = 60) {
this.stop();
timer = tm;
fnProc();
},
stop: function () {
if (timerId !== -1) {
clearTimeout(timerId);
timerId = -1;
}
}
}
})();
Loop.start(timerInSec);
setTimeout(() => {
Loop.start(timerInSec);
}, 500);
I'm building a DTMF dial emulator. After a certain function runs, the function dial() is executed. Currently, I hear the ringback tone once but I cannot get it to ring twice.
I originally tried to get the same file to play twice by resetting the audio currentTime to 0 and playing it again after a delay of 4 seconds (the duration between ringback tones in North America).
That didn't work so I thought perhaps JS didn't want me playing the same audio twice. So I recreated it again as a second variable and created a new function for that next in the sequence for dial().
That didn't work, but if I added an alert box before ringingTone2 played, it played as soon as I dismissed the alert.
Obviously, I can't have an alert dialog when this is done. How can I have the sound play twice, but with a 4 second gap in between? I've had no success with setTimeout(ring(), 4000) either.
Here is some of the code pertaining to this issue:
var ringingTone1 = new Audio('DTMF-ringbackTone.mp3');
var ringingTone2 = new Audio('DTMF-ringbackTone.mp3');
function dial() {
ring();
function ring() {
ringingTone1.play();
setTimeout(ringingTone2.play(),4000);
}
Right now, JS basically plays the ringback tone once and moves onto to what is after this in the script.
The following causes it to stop working:
function dial() {
ring();
function ring() {
var played = 0;
var maxPlay = 2;
var ringingTone = document.getElementById('music');
var playBtn = document.getElementById('playbtn');
ringingTone.onplay = ring() {
//played counter
played++;
};
ringingTone.addEventListener("ended", ring() {
//reset to start point
ringingTone.currentTime = 0;
if (played < maxPlay) {
ringingTone.play();
} else {
played = 0;
}
});
playBtn.addEventListener("click", ring() {
ringingTone.play();
});
}
This is my complete script:
var availableNumbers = ["0", "911"];
function numberSuggestion() {
var randomNumber = Math.random() * availableNumbers.length -1;
var suggestedNumber = availableNumbers[randomNumber];
document.getElementById("suggestion").innerHTML = "How about dialing " + suggestedNumber + "? Don't like this number? Click the button above again!";
}
var dialTone;
function offHook() {
document.getElementById("WE2500").style.display = "none";
document.getElementById("dialPad").style.display = "block";
dialTone = new Audio('dialTone.m4a');
dialTone.play();
}
var number = "";
var timeout;
function numberDial() {
if (dialTone) {
dialTone.pause();
dialTone.currentTime = 0;
}
clearTimeout(timeout);
timeout = setTimeout(dial, 2000);
}
function dial1() {
numberDial();
number = number + "1";
var tone1 = new Audio('DTMF-1.wav');
tone1.play();
}
function dial2() {
numberDial();
number = number + "2";
var tone2 = new Audio('DTMF-2.wav');
tone2.play();
}
function dial3() {
numberDial();
number = number + "3";
var tone3 = new Audio('DTMF-3.wav');
tone3.play();
}
function dial4() {
numberDial();
number = number + "4";
var tone4 = new Audio('DTMF-5.wav');
tone4.play();
}
function dial5() {
numberDial();
number = number + "5";
var tone5 = new Audio('DTMF-5.wav');
tone5.play();
}
function dial6() {
numberDial();
number = number + "6";
var tone6 = new Audio('DTMF-6.wav');
tone6.play();
}
function dial7() {
numberDial();
number = number + "7";
var tone7 = new Audio('DTMF-7.wav');
tone7.play();
}
function dial8() {
numberDial();
number = number + "8";
var tone8 = new Audio('DTMF-8.wav');
tone8.play();
}
function dial9() {
numberDial();
number = number + "9";
var tone9 = new Audio('DTMF-9.wav');
tone9.play();
}
function dial0() {
numberDial();
number = number + "0";
var tone0 = new Audio('DTMF-0.wav');
tone0.play();
}
function dialStar() {
numberDial();
number = number + "*";
var toneStar = new Audio('DTMF-star.wav');
toneStar.play();
}
function dialPound() {
numberDial();
number = number + "#";
var tonePound = new Audio('DTMF-pound.wav');
tonePound.play();
}
//var ringingTone1 = new Audio('DTMF-ringbackTone.mp3');
//var ringingTone2 = new Audio('DTMF-ringbackTone.mp3');
function dial() {
ring();
function ring() {
var played = 0;
var maxPlay = 2;
var ringingTone = document.getElementById('music');
var playBtn = document.getElementById('playbtn');
ringingTone.onplay = ring() {
//played counter
played++;
};
ringingTone.addEventListener("ended", ring() {
//reset to start point
ringingTone.currentTime = 0;
if (played < maxPlay) {
ringingTone.play();
} else {
played = 0;
}
});
playBtn.addEventListener("click", ring() {
ringingTone.play();
});
}
switch(number) {
case "0":
break;
case "911":
var pickup911 = new Audio('911-xxx-fleet.mp3');
pickup911.play();
break;
default:
}
}
The idea is simple, one counter for played time, one constant for max play.
Use the onplay event to increase the counter everytime a audio played.
Use the ended event to replay the audio if max play time not reached yet. Otherwise, set played count to 0.
var played = 0;
var maxPlay = 2;
var ringingTone = document.getElementById('music');
var playBtn = document.getElementById('playbtn');
ringingTone.onplay = function() {
//played counter
played++;
};
ringingTone.addEventListener("ended", function() {
//reset to start point
ringingTone.currentTime = 0;
if (played < maxPlay) {
ringingTone.play();
} else {
played = 0;
}
});
playBtn.addEventListener("click", function() {
ringingTone.play();
});
<audio id="music" src="http://www.noiseaddicts.com/samples_1w72b820/3732.mp3"></audio>
<button id="playbtn">Play me</button>
Calling a function in setTimeout (and I think it applies to all callbacks) using () will cause the code inside to be evaluated immediately (which in your case meant play both audios at the same time).
Hence my comment above to use .play without (), which looks like doesn't work. However, this does work:
function dial() {
function ring() {
ringingTone.play();
}
ring();
setTimeout(ring, 4000);
}
dial();
http://jsfiddle.net/tL3monyp/
(audio src unscrupulously copied from Daniel's answer ;-)
I've also added 2 timeouts to illustrate calling the function with and without ().
Need some help with my code, I can't get my alerts to work with my countdown timer. They should be alerting at 4,3,2 minutes left on the timer. I currently can't get the alerts to fire at all, sometimes they would fire but each second after 4, the alert for "4" would fire. I need it to just go once... Any help would be appreciated
Heres my script
var running=false
var endTime=null
var timerID=null
function startTimer(){
running=true
now=new Date()
now=now.getTime()
endTime=now+(1000*60*5)
showCountDown()
}
function showCountDown(){
var now=new Date()
now=now.getTime()
if (endTime-now<=239990 && endTime-now>240010){alert("4")};
if (endTime-now<=179990 && endTime-now>180010){alert("3")};
if (endTime-now<=119990 && endTime-now>120010){alert("2")};
if (endTime-now<=0){
stopTimer()
alert("Time is up. Put down pencils")
} else {
var delta=new Date(endTime-now)
var theMin=delta.getMinutes()
var theSec=delta.getSeconds()
var theTime=theMin
theTime+=((theSec<10)?":0" : ":")+theSec
document.forms[0].timerDisplay.value=theTime
if (running){
timeID=setTimeout("showCountDown()",1000)
}
}
}
function stopTimer(){
clearTimeout(timeID)
running=false
document.forms[0].timerDisplay.value="0.00"
}
Update, Sorry meant minutes instead of seconds
Update 2: Change the ifs, now they fire but keep firing after the 4 second mark
if (endTime-now<=240010 && endTime-now<=239990){alert("4")};
if (endTime-now<=180010 && endTime-now<=179990){alert("3")};
if (endTime-now<=120010 && endTime-now<=119990){alert("2")};
Why are you calling clearTimeout? setTimeout invokes its callback only once. There is no need to clear it. Also you could just have a variable that stores the minutes until the end of the countdown and decrement that by one in each iteration.
The simplest solution might look like this
function startTimer(minutesToEnd) {
if(minutesToEnd > 0) {
if(minutesToEnd <= 4) {
console.log(minutesToEnd);
}
setTimeout(startTimer, 60000, minutesToEnd - 1);
} else {
console.log("Time is up. Put down pencils")
}
}
I actually spent some time working on this. I have no idea if this is what you wanted, but I created a timer library. I have a working demo for you. I had fun making this. Let me know what you think:
JS:
(function () {
var t = function (o) {
if (!(this instanceof t)) {
return new t(o);
}
this.target = o.target || null;
this.message = o.message;
this.endMessage = o.endMessage;
//setInterval id
this.si = -1;
//Initial start and end
this.startTime = null;
this.endTime = null;
this.interTime = null;
this.duration = o.duration || 1000 * 60 * 5;
//looping speed miliseconds it is best to put the loop at a faster speed so it doesn't miss out on something
this.loop = o.loop || 300;
//showing results miliseconds
this.show = o.show || 1000;
};
t.fn = t.prototype = {
init: function () {}
};
//exporting
window.t = t;
})();
//Timer Functions ---
t.fn.start = function () {
this.startTime = new Date();
this.interTime = this.startTime.getTime();
this.endTime = new Date().setMilliseconds(this.startTime.getMilliseconds() + this.duration);
//returns undefined... for some reason.
console.log(this.endTime);
var $this = this;
this.writeMessage(this.duration);
this.si = setInterval(function () {
var current = new Date(),
milli = current.getTime();
if (milli - $this.interTime >= $this.show) {
var left = $this.endTime- milli;
if (left <= 0) {
$this.stop();
} else {
$this.interTime = milli;
$this.writeMessage(left);
}
}
}, this.loop);
return this;
};
t.fn.writeMessage = function(left){
this.target.innerHTML = this.message + ' ' + Math.floor(left / 1000);
return this;
};
t.fn.stop = function () {
//stopping the timer
clearInterval(this.si);
this.target.innerHTML = this.endMessage;
return this;
};
//Not chainable
t.fn.isRunning = function () {
return this.timer > -1;
};
var timer = t({
target: document.getElementById('results'),
loop: 50,
duration: 10000,
show: 1000, //default is at 1000 miliseconds
message: 'Time left: ', //If this is ommited then only the time left will be shown
endMessage: 'Time is up. Put down your pencils'
}).start();
document.getElementById('stop').onclick = function(){
timer.stop();
};
HTML:
<div id="results"></div>
<button id="stop">Stop</button>
Demo here
Update: I added some stuff
Demo 2
Update 2: I fixed the bug where 10 would hop straight to 8
Demo 3
window.setInterval(function(){
//do stuff
}, milisec);
Is there a way to stop this interval at will, and to resume it from where it lasted? Say, code runs every 5 sec. I stop it in the middle of the 2nd second, when resumed, I want it to run the remaining 3 seconds and continue to run afterwards every 5 sec. again.
Try this:
1- when you want to pause the timer, calculate the remaining milliseconds and store it somewhere then call clearInterval.
2- When you want to resume the timer, just make a call to setTimeout passing the remaining time stored in the previous step as the argument.
3- And in setTimeout's callback you should call setInterval again.
UPDATE: This is what you want, a changed version of javascript: pause setTimeout(); thanks to #Felix Kling
function IntervalTimer(callback, interval) {
var timerId, startTime, remaining = 0;
var state = 0; // 0 = idle, 1 = running, 2 = paused, 3= resumed
this.pause = function () {
if (state != 1) return;
remaining = interval - (new Date() - startTime);
window.clearInterval(timerId);
state = 2;
};
this.resume = function () {
if (state != 2) return;
state = 3;
window.setTimeout(this.timeoutCallback, remaining);
};
this.timeoutCallback = function () {
if (state != 3) return;
callback();
startTime = new Date();
timerId = window.setInterval(callback, interval);
state = 1;
};
startTime = new Date();
timerId = window.setInterval(callback, interval);
state = 1;
}
Usage:
var timer = new IntervalTimer(function () {
alert("Done!");
}, 5000);
window.setTimeout(function () {
timer.pause();
window.setTimeout(function () {
timer.resume();
}, 5000);
}, 2000);
To piggyback off Alireza's answer, here's an ES6 class that does the same thing with a bit more functionality, and doesn't start right away. You can set a maximum number of times the timer will fire off before automatically stopping, and pause and resume any number of times before the next time it's set to fire off.
export default class IntervalTimer{
constructor(name, callback, interval, maxFires = null){
this.remaining = 0;
this.state = 0; // 0 = idle, 1 = running, 2 = paused, 3= resumed
this.name = name;
this.interval = interval; //in ms
this.callback = callback;
this.maxFires = maxFires;
this.pausedTime = 0; //how long we've been paused for
this.fires = 0;
}
proxyCallback(){
if(this.maxFires != null && this.fires >= this.maxFires){
this.stop();
return;
}
this.lastTimeFired = new Date();
this.fires++;
this.callback();
}
start(){
this.log.info('Starting Timer ' + this.name);
this.timerId = setInterval(() => this.proxyCallback(), this.interval);
this.lastTimeFired = new Date();
this.state = 1;
this.fires = 0;
}
pause(){
if (this.state != 1 && this.state != 3) return;
this.log.info('Pausing Timer ' + this.name);
this.remaining = this.interval - (new Date() - this.lastTimeFired) + this.pausedTime;
this.lastPauseTime = new Date();
clearInterval(this.timerId);
clearTimeout(this.resumeId);
this.state = 2;
}
resume(){
if (this.state != 2) return;
this.pausedTime += new Date() - this.lastPauseTime;
this.log.info(`Resuming Timer ${this.name} with ${this.remaining} remaining`);
this.state = 3;
this.resumeId = setTimeout(() => this.timeoutCallback(), this.remaining);
}
timeoutCallback(){
if (this.state != 3) return;
this.pausedTime = 0;
this.proxyCallback();
this.start();
}
stop(){
if(this.state === 0) return;
this.log.info('Stopping Timer %s. Fired %s/%s times', this.name, this.fires, this.maxFires);
clearInterval(this.timerId);
clearTimeout(this.resumeId);
this.state = 0;
}
//set a new interval to use on the next interval loop
setInterval(newInterval){
this.log.info('Changing interval from %s to %s for %s', this.interval, newInterval, this.name);
//if we're running do a little switch-er-oo
if(this.state == 1){
this.pause();
this.interval = newInterval;
this.resume();
}
//if we're already stopped, idle, or paused just switch it
else{
this.interval = newInterval;
}
}
setMaxFires(newMax){
if(newMax != null && this.fires >= newMax){
this.stop();
}
this.maxFires = newMax;
}
}
You should only need setTimeout with a go and stop - http://jsfiddle.net/devitate/QjdUR/1/
var cnt = 0;
var fivecnt = 0;
var go = false;
function timer() {
if(!go)
return;
cnt++;
if(cnt >= 5){
cnt=0;
everyFive();
}
jQuery("#counter").text(cnt);
setTimeout(timer, 1000);
}
function everyFive(){
fivecnt++;
jQuery("#fiver").text(fivecnt);
}
function stopTimer(){
go = false;
}
function startTimer(){
go = true;
timer();
}
let time = document.getElementById("time");
let stopButton = document.getElementById("stop");
let playButton = document.getElementById("play");
let timeCount = 0,
currentTimeout;
function play_pause() {
let status = playButton.innerHTML;
if (status == "pause") {
playButton.innerHTML = "Resume";
clearInterval(currentTimeout);
return;
}
playButton.innerHTML = "pause";
stopButton.hidden = false;
clearInterval(currentTimeout);
currentTimeout = setInterval(() => {
timeCount++;
const min = String(Math.trunc(timeCount / 60)).padStart(2, 0);
const sec = String(Math.trunc(timeCount % 60)).padStart(2, 0);
time.innerHTML = `${min} : ${sec}`;
}, 1000);
}
function reset() {
stopButton.hidden = true;
playButton.innerHTML = "play";
clearInterval(currentTimeout);
timeCount = 0;
time.innerHTML = `00 : 00`;
}
<div>
<h1 id="time">00 : 00</h1>
<br />
<div>
<button onclick="play_pause()" id="play">play</button>
<button onclick="reset()" id="stop" hidden>Reset</button>
</div>
</div>