How to do a countdown with randomized time intervals, stopping at zero? - javascript

i would like to add another counter in this code:
function animateValue(id) {
var obj = document.getElementById(id);
var counter = getLocalStoregaValue();
var current = counter ? +counter : obj.innerHTML;
obj.innerHTML = counter;
setInterval(function() {
var counter = current--;
obj.innerHTML = counter;
localStorage.setItem('counter', counter);
}, 1000);
}
function getLocalStoregaValue() {
return localStorage.getItem('counter');
}
animateValue('value');
I would like it to scale once every second (as in this case) and once every 5 seconds. How can I? And then, how can I make it stop at 0? So without negative numbers. Thank you very much.
EDIT: I explained myself wrong.
I would like a single counter that drops in number from a minimum of 10 to a maximum of 20.
Example: the counter marks 50. After 15 seconds it marks 49. After 18 seconds it marks 48. After 11 seconds it marks 47. And so up to 0.
I hope I explained myself well this time :)

Ok, I was interrupted while posting my answer. Here now the explanation:
I left out the localStorage part of your question and concentrated on the generation of "independent countdowns" first:
function cntdwn(sel,stp,intv){
let el=document.querySelector(sel),
n=el.textContent-stp,
cd=setInterval(()=>{
el.textContent=n;
if((n-=stp)<0) clearInterval(cd);
}, intv);
}
cntdwn('#one',1,1000) ;
setTimeout(()=>cntdwn('#two',1,3000), 12000);
<p>first countdown:<br>step: 1, interval: 1s</p>
<p id="one">15</p>
<p>second countdown:<br>step: 1, action after: 15, 18, 21, 24 ... s (as mentioned in comment)</p>
<p id="two">50</p>
The cntdwn() function provides a scope in which individual countdowns can be set up for arbitrary DOM elements, each with their own counter (it starts with the value found in the DOM element), step-width and interval (in milliseconds).
Each countdown is generated with let cd=setInterval(...). The reference cd can then be used to stop the countdown (in clearInterval(cd)), once the value of n is found to be below zero.
Edit:
Assuming you made a typo in your sequence of intervals and you really meant: 15, 18, 21 seconds, then the edited second countdown should be the correct solution.
I used a setTimeout() function to delay the action by 12 seconds, then, after the first of the regular 3 second intervals (i. e. after a total of 15 seconds) the first change occurs. The countdown then continues in 3 second intervals until it reaches zero.
Yet another edit:
Ok, so you want: "A countdown with random time intervals (range 10 to 20s each) that will stop at zero"
This should do it:
function cntdwn(sel,int1,int2){
let el=document.querySelector(sel),
n=el.textContent-1,
cd=()=>setTimeout(()=>{
el.textContent=n;
if(n--) cd();
}, 1000*(int1+Math.random()*(int2-int1)));
cd();
}
cntdwn('#one',10,20);
<p>countdown:<br>step: 1, intervals: between 10 and 20 s</p>
<p id="one">5</p>

If you can use ES2017, you can use an asynchronous function to do it, like this:
async function animateValue(id) {
function timeout(t){
return new Promise(r => setTimeout(r, t))
}
var obj = document.getElementById(id);
var counter = getLocalStoregaValue();
for(let i = +counter || +obj.innerHTML || 0; i >= 0; i--){
obj.innerHTML = i;
localStorage.setItem('counter', i);
await timeout((Math.random() * 10 + 10) * 1000); //Pause for 10 to 20 seconds. For an integer second value, wrap `Math.random() * 10` into a `Math.floor` call
};
}
function getLocalStoregaValue() {
return localStorage.getItem('counter');
}
animateValue('value').catch(console.error);
<div id="value">50</div>
Try it (I commented out the localStorage part, as it isn't allowed in Stack Snippets):
async function animateValue(id) {
function timeout(t){
return new Promise(r => setTimeout(r, t))
}
var obj = document.getElementById(id);
var counter = getLocalStoregaValue();
for(let i = +counter || +obj.innerHTML || 0; i >= 0; i--){
obj.innerHTML = i;
//localStorage.setItem('counter', i);
await timeout((Math.random() * 10 + 10) * 1000); //Pause for 10 to 20 seconds. For an integer second value, wrap `Math.random() * 10` into a `Math.floor` call
};
}
function getLocalStoregaValue() {
//return localStorage.getItem('counter');
}
animateValue('value').catch(console.error);
<div id="value">50</div>

Related

How to Change Interval Time Dynamically in For Loop According to Index/iteration Number?

Since I could not comment, I am forced to write this post. I got the below code which delays/waits exactly 1 seconds or 1000 milliseconds -
let n = 5;
for (let i=1; i<n; i++)
{
setTimeout( function timer()
{
console.log("hello world");
}, i*1000 );
}
But how can I delay it i*1000 seconds instead of fixed 1000 milliseconds so the waiting depends on iteration number ?
For example, if n= 5 , then I want the loop delay 1 second in 1st iteration. 2 seconds in second iteration, and so on.. the final delay will 5 seconds.
While this task could be solved with promises, reactive streams and other cool tools (hey, nobody has suggested using workers yet!), it can also be solved with a little arithmetics.
So you want timeouts in a sequence: 1s, the previous one + 2s, the previous one + 3s, and so on. This sequence is: 1, 3, 6, 10, 15... and its formula is a[n] = n * (n + 1) / 2. Knowing that...
let n = 6;
console.log(new Date().getSeconds());
for (let i = 1; i < n; i++) {
setTimeout(function timer() {
console.log(new Date().getSeconds());
}, 1000 * i * (i + 1) / 2);
}
Here is a function that will show immediately, then 1 second later, 2 seconds after than, 3 seconds after that etc. No special math, no promises needed
const n = 5;
let cnt=0;
function show() {
console.log("call "+cnt,"delay: ",cnt,"sec");
cnt++;
if (cnt > n) return; // we are done
setTimeout(show, cnt*1000 ); // cnt seconds later
}
show()
You can try using async/await (Promises), to serialize your code:
const waitSeconds = seconds => new Promise(resolve => setTimeout(resolve, seconds))
async function main () {
let oldDate = new Date()
let newDate
/*
* If you put 'await' inside the loop you can synchronize the async code, and simulate
* a sleep function
*/
for (let i=1; i<5; i++) {
await waitSeconds(i*1000)
newDate = new Date()
console.log(`Loop for i=${i}, elapsed=${moment(newDate).diff(oldDate, 'seconds')} seconds`)
oldDate = newDate
}
console.log('End')
}
main()
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
Took me some time to decipher your question xD, but is this what you want?
This will keep firing console.log with i*1000 delay each time.
so the first time it will be 1 second long (1*1000), next it will be 2 seconds and so on.
let i = 0;
loop = () => {
setTimeout(() => {
console.log(new Date()); // for clarity
i++;
if (i < 10) {
loop();
}
}, i * 1000)
};
loop();
Loop doesn't waits for timeout function to be completed.
So, when the loop runs it schedules your alert for each index.
You can use a function which will run according to your index but scheduled at same time. You can feel the difference of 3 seconds.
function test(i){
setTimeout( function timer(){
console.log("hello world" + i);
}, i*3000);
}
for (let i=1; i<4; i++) {
test(i);
}
Use recursive calls instead of for loop
let i=1;
function a(i) {
if (i > 5)
return
else
b("message", i)
}
function b(s, f) {
setTimeout(function timer() {
console.log(s + " " + f + " seconds");
}, f * 1000);
a(++i);
}
a(i);

Making a Javascript that are adding a number to an another number then adding it again to their result and so on

I'm trying to make a script that are adding 10 to 10, then 10 to 20, then 10 to 30, and so on.
I mean, to auto count, by 10 by 10.
The problem is that you are using the ++ operator, witch increments the variable by 1. Just use this:
function init() {
var n = 0;
e = document.getElementById("output");
setInterval(function() { e.innerHTML = n = n + 10; }, 1000);
}
window.onload = init;
You could take instead of an increment operator ++
++n
an addition assignment +=
n += 100
with a fixed value for addition.
For getting a number which has some kind of easing, you could round the number.
function getRound(v) {
var i = Math.floor(Math.log10(v) - 3),
f = Math.pow(10, i);
return Math.round(v / f) * f;
}
function init() {
var n = 0, // declare both
e = document.getElementById("output"); // variables
setInterval(function() {
n += 29979245.8; // increment by the wanted value
e.innerHTML = getRound(n); // assign value to element
}, 100);
}
window.onload = init;
<div id="output"></div>
Gathering from your comments, what you seem to actually want is something like this:
var universe = 0;
show_value();
setInterval(function() {
universe += 299792.458;
show_value();
}, 1);
function show_value() {
display = universe.toFixed(5)
document.getElementById("universe").innerHTML = display + " meters!";
}
This jsfiddle shows what it does. I hope I have understood the effect you were hoping for.
The reason your code wasn't working was because of a cardinal misunderstanding of setTimeout; the second arguement is the number of milliseconds to wait to run the first arguement, your function. You currently have it set to 1000 which is a second, but you want it to run more times a second to give a "eased" effect.
You seem to want to do this in increments of 10, but you'd have to run your function 29979245.8 times a second, or around every 33.35 microseconds. So instead, in my example it runs every millisecond, adding 299792.458 every time instead of 10 to account for that. To make it so that the number doesn't "jump" I also used toFixed.

How do I delay this code running in JavaScript?

I have written this code to change an image:
change = function(){
for (r=0; r<6; r++){
for (i = 0; i < 6 ; i++) {
setInterval(imgfile(number=i+1), 5000);
}
}
}
imgfile= function(number){
a = 'document.getElementById("imgdiv").src = "images/'+number+'.svg"';
eval(a);
}
The function change() is called when a button is clicked.
When I press the button the image changes straight to 6.svg, when I want it to go through the images 1, 2, 3, 4, 5, 6 and to repeat it 6 times. When I change setInterval to change.setInterval or imgfile.setInterval it doesn't work at all. How do I fix this?
change = function(i=0){
imgfile(i%6+1);//change image
if(i<36) setTimeout(change,5000,i+1);//next image in 5 seconds
}
imgfile= function(number){
document.getElementById("imgdiv").src = "images/"+number+".svg";//no need to use ev(i||a)l
}
Instead of loop/interval mess you can simply start a timeout that restarts itself after changing the image... This code will loop over 6 images with a delay of 5 seconds and that 6 times...
Something like this, perhaps?
var index, imgCount, loopCount, imgTag, countdown;
index = 0;
imgCount = 6;
loopCount = 6;
imgTag = document.getElementById('imgdiv');
countdown = function () {
if (index < imgCount * loopCount) {
imgTag.src = 'images/' + index % imgCount + '.svg';
index = index + 1;
setTimeout(countdown, 5000);
}
};
countdown();
Here we're avoiding the double loop and using modular math (index % imgCount) to get the right file number.
For another question I wrote a nice utility function that has quite a number of uses, but can also handle this scenario very easily. The main issue is that there is no time elapsing between the different delays being set. So you are setting 6 different actions to all happen within 5000ms, and all will occur at the same moment.
Here's my original answer
Here's the utility function for that answer, along with its application to your problem.
function doHeavyTask(params) {
var totalMillisAllotted = params.totalMillisAllotted;
var totalTasks = params.totalTasks;
var tasksPerTick = params.tasksPerTick;
var tasksCompleted = 0;
var totalTicks = Math.ceil(totalTasks / tasksPerTick);
var initialDelay = params.initialDelay;
var interval = null;
if (totalTicks === 0) return;
var doTick = function() {
var totalByEndOfTick = Math.min(tasksCompleted + tasksPerTick, totalTasks);
do {
params.task(tasksCompleted++);
} while(tasksCompleted < totalByEndOfTick);
if (tasksCompleted >= totalTasks) clearInterval(interval);
};
// Tick once immediately, and then as many times as needed using setInterval
if (!initialDelay) doTick();
if (tasksCompleted < totalTicks) interval = setInterval(doTick, totalMillisAllotted / totalTicks);
}
// Do 6 actions over the course of 5000 x 6 milliseconds
doHeavyTask({
totalMillisAllotted: 5000 * 6,
totalTasks: 6,
tasksPerTick: 1,
initialDelay: false, // Controls if the 1st tick should occur immediately
task: function(n) { console.log('Set image to "images/' + (n + 1) + '.svg"'); }
});
You want to do setTimeout().
setTimeout pauses for the millesecond value and then does the code. Where setInterval runs the code every whatever milleseconds.
Yeah, don't do change.setInterval or whatever, it is just setInterval.
An example for you would be this inside the for loop to replace the setInterval function.
setTimeout(imgfile(i+1), 5000);

Javascript decimal increment?

I've got the following variable JS:
http://jsfiddle.net/c8u8wLsL/13/
$(document).ready(function () {
var total = 15.5,
value = 0,
elem = $('div');
var interval = setInterval(function () {
elem.text(value.toFixed(1) + '$');
if (value >= total) {
clearInterval(interval);
}
value = value + 0.1;
}, 5);
});
Two questions:
The resulting number is 15.6 why?
How can I make the incrementation spend the same amount of time from 0 to the target value? (from 0 to 25 spends the same time as from 0 to 250)
You forget to exit from function. Also you should probably update node's text after checking total.
if (value >= total) {
return clearInterval(interval);
}
elem.text(value.toFixed(1) + '$');
fiddle http://jsfiddle.net/Lqxsh39q/
To solve second problem you can pre-calculate duration of each interval before it setup. And use it like second argument in setInterval. Something like duration = 1000 / (total * 10); or any formula that you want.
fiddle: http://jsfiddle.net/Lqxsh39q/1/
#Glen Swift's answer is correct but I have to point out regarding your original code:
You get the resulting number as 15.6 because:
When you think you are getting 15.5 as the result, you are actually getting 15.4999999, which is smaller than 15.5 and hence the if condition is false even if you think it is true. So it gets incremented once again, giving the final result as 15.6.
As far as the second part is concerned, to get the same time, you need to have the same number of steps for each addition rather than the fixed 0.1. Let's say you want to reach the target in 100 steps everytime, you can divide the total interval by 100 and then replace it in the code where you are writing 0.1 currently.
The final code should look something like:
$(document).ready(function () {
var total = 15.5,
value = 0,
ment=(total-value)/100,
elem = $('div');
var interval = setInterval(function () {
if (value >= total) {
return clearInterval(interval);
}
elem.text(value.toFixed(1) + '$');
value = value + ment;
}, 5);
});
See the fiddle here
Your existing code:
var interval = setInterval(function () {
elem.text(value.toFixed(1) + '$');
if (value >= total) {
clearInterval(interval);
}
value = value + 0.1;
}, 5);
It should check for the >= total condition first. if condition fails then exit the function.
But you are modifying the text element before the check. thus your error.
This would do.
var interval = setInterval(function () {
value = value + 0.1;
if (value <= total) {
elem.text(value.toFixed(1) + '$');
} else {
clearInterval(interval);
}
}, 5);

Increment integer by 1; every 1 second

My aim is to create identify a piece of code that increments a number by 1, every 1 second:
We shall call our base number indexVariable, I then want to: indexVariable = indexVariable + 1 every 1 second; until my indexVariable has reached 360 - then I wish it to reset to 1 and carry out the loop again.
How would this be possible in Javascript? - if it makes a difference I am using the Raphael framework.
I have carried out research of JavaScript timing events and the Raphael delay function - but these do not seem to be the answer - can anyone assist?
You can use setInterval() for that reason.
var i = 1;
var interval = setInterval( increment, 1000);
function increment(){
i = i % 360 + 1;
}
edit: the code for your your followup-question:
var interval = setInterval( rotate, 1000);
function rotate(){
percentArrow.rotate(1,150,150);
}
I'm not entirely sure, how your rotate works, but you may have to store the degrees in a var and increment those var too like in the example above.
var indexVariable = 0;
setInterval(function () {
indexVariable = ++indexVariable % 360 + 1; // SET { 1-360 }
}, 1000);
Try:
var indexVariable = 0;
setInterval(
function () {
indexVariable = (indexVariable + 1) % 361;
}, 1000}

Categories

Resources