Reputation: 641
I would like to imitate progress bar getting random lengh added 0% to 100% with series of messages displayed in certain sequence but also in random time.
In order to do that I'm staring two independent loops using JS setTimeout method with a random number genearted inside of each loop. But each loop not supposed to be longer than 30 seconds and with the way I'm doing I can't control that.
For the second loop I have to fire the function for 11 times randomly but fit that into 30 seconds as well.
The progress bar moves by changing its width. The messages supposed to be right above that. It looks like this:
HTML:
<span id="progressMsg"></span>
<input type="hidden" id="progressMsgNumber">
<div class="pogress progress-info slim" id="progressBar" style="width=1%"></div>
JS:
setTimeout(progressBar, 1000);
setTimeout(progressMsn, 1000);
function progressBar() {
barWidth = document.getElementById("progressBar").style.width.slice(0, -1); //to get number without "%" char
if (barWidth >=100) {
// We are done
} else {
minRand = 1;
maxRand = 5;
randSeconds = Math.floor(Math.random() * (maxRand - minRand + 1)) + minRand;
barWidth = Number(barWidth)+randSeconds;
document.getElementById("progressBar").style.width = barWidth+"%";
setTimeout(progressBar, randSeconds*1000);
}
}
function progressMsg() {
msgList = new array ("Msg1", "Msg2", ... "Msg11");
barWidth = document.getElementById("progressBar").style.width.slice(0, -1);
if (barWidth >=100) {
// We are done
} else {
msgNumber = document.getElementById("progressMsgNumber").val;
msgNumber++;
document.getElementById("progressMsg").innerHTML = msgList[msgNumber];
document.getElementById("progressMsgNumber").val = msgNumber;
minRand = 1;
maxRand = 5;
randSeconds = Math.floor(Math.random() * (maxRand - minRand + 1)) + minRand;
setTimeout(progressMsg, randSeconds*1000);
}
}
Upvotes: 0
Views: 126
Reputation: 1
The key is to create ALL the random times beforehand, then you can manipulate the times in such a way as to guarantee exact 30 seconds total time.
Note: code below, for demo purposes, has times 1/10th of what you would use, i.e. you'd want
minRand = 1000,
maxRand = 5000,
maxTime = 30000;
function progressMsg() {
const msgList = ["Msg1", "Msg2", "Msg3", "Msg4", "Msg5", "Msg6", "Msg7", "Msg8", "Msg9", "Msg10", "Msg11"],
minRand = 100,
maxRand = 500,
maxTime = 3000;
let triggers = Array.from({length:11}, () => Math.floor(Math.random() * (maxRand - minRand + 1)) + minRand);
triggers.forEach((n, i, a) => a[i] = (a[i-1]||0) + a[i]);
const mult = maxTime / triggers.slice(-1);
triggers = triggers.map(v => Math.floor(Math.ceil(v * mult)));
const update = (t, i) => {
console.log(t, Math.round(t/maxTime * 100) + '%', msgList[i]);
}
triggers.forEach((n, i) => setTimeout(update, n, n, i));
}
progressMsg();
to make it work with your code
function progressMsg() {
const msgList = ["Msg1", "Msg2", "Msg3", "Msg4", "Msg5", "Msg6", "Msg7", "Msg8", "Msg9", "Msg10", "Msg11"],
minRand = 1000,
maxRand = 5000,
maxTime = 30000;
// create an array of random times, e.g. 1,3,4,2,1...
let triggers = Array.from({length:11}, () => Math.floor(Math.random() * (maxRand - minRand + 1)) + minRand);
// accumulate times, so we'd have e.g. 1,4,8,10,11...
triggers.forEach((n, i, a) => a[i] = (a[i-1]||0) + a[i]);
const mult = maxTime / triggers.slice(-1);
// adjust so the last time is exactly maxTime
triggers = triggers.map(v => Math.floor(Math.ceil(v * mult)));
const update = i => {
document.getElementById("progressMsg").innerHTML = msgList[i];
document.getElementById("progressMsgNumber").val = i;
}
// start all the timeouts at once, because we have cumulative times
triggers.forEach((n, i) => setTimeout(update, n, i));
}
function progressBar() {
const minRand = 1000,
maxRand = 5000,
maxTime = 30000,
bar = document.getElementById("progressBar");
let time = 0;
let triggers = [];
// here we just keep adding random times until we reach maxTime
while (time < maxTime) {
triggers.push(time += Math.floor(Math.random() * (maxRand - minRand + 1)) + minRand);
}
const mult = maxTime / triggers.slice(-1);
// adjust times so last time is exactly maxTime
triggers = triggers.map(v => Math.floor(Math.ceil(v * mult)));
const update = t => {
bar.style.width = (100*t/maxTime) + '%';
}
triggers.forEach(n => setTimeout(update, n, n));
}
In answer to comment - one way to know when all is done is using promises
const delay = (fn, delay, ...params) => new Promise(resolve => setTimeout(fn, delay, ...params));
function progressMsg() {
const msgList = ["Msg1", "Msg2", "Msg3", "Msg4", "Msg5", "Msg6", "Msg7", "Msg8", "Msg9", "Msg10", "Msg11"],
minRand = 1000,
maxRand = 5000,
maxTime = 30000;
// create an array of random times, e.g. 1,3,4,2,1...
let triggers = Array.from({length:11}, () => Math.floor(Math.random() * (maxRand - minRand + 1)) + minRand);
// accumulate times, so we'd have e.g. 1,4,8,10,11...
triggers.forEach((n, i, a) => a[i] = (a[i-1]||0) + a[i]);
const mult = maxTime / triggers.slice(-1);
// adjust so the last time is exactly maxTime
triggers = triggers.map(v => Math.floor(Math.ceil(v * mult)));
const update = i => {
document.getElementById("progressMsg").innerHTML = msgList[i];
document.getElementById("progressMsgNumber").val = i;
}
// start all the timeouts at once, because we have cumulative times
return Promise.all(triggers.map((n, i) => delay(update, n, i)));
}
function progressBar() {
const minRand = 1000,
maxRand = 5000,
maxTime = 30000,
bar = document.getElementById("progressBar");
let time = 0;
let triggers = [];
// here we just keep adding random times until we reach maxTime
while (time < maxTime) {
triggers.push(time += Math.floor(Math.random() * (maxRand - minRand + 1)) + minRand);
}
const mult = maxTime / triggers.slice(-1);
// adjust times so last time is exactly maxTime
triggers = triggers.map(v => Math.floor(Math.ceil(v * mult)));
const update = t => {
bar.style.width = (100*t/maxTime) + '%';
}
return Promise.all(triggers.map(n => delay(update, n, n)));
}
// usage
Promise.all([progressMsg(), progressBar()]).then(() => {
// all done here
});
Upvotes: 1
Reputation: 502
you can store the full spent time and number of displayed messages in variables
let spentTime = 0; // Declare
let displayedMessages = 0;
function progressMsg() {
....
setTimeout(() => { // Edit
progressMsg();
spentTime += randSecods; // add the seconds of this tick
displayedMessages++;
}, randSeconds*1000);
}
}
you can then use it to change the min and max in any tick passed on the spent time and the left messages something like
maxSeconds = (fullTime - spentTime) / (numOfMessages - displayedMessages)
will make sure all the messages are displayed, you can change the min value too, to seem more naturally.
Upvotes: 1
Reputation: 61
You can wrap your functions into Promise, with one additional Promise to resolve itself in 30 seconds, and then use Promise.race See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
Upvotes: -1