I want to update my progress bar in html while function is running. Is there any way I can do this?
function animateProgressBar() {
var numIteration = 100;
for (var i = 1; i <= numIteration; i++) {
//do some calculations
width = (i/numIteration)*100;
htmlElement.style.width = width + '%';
}
}
The problem is that your code is blocking. Therefore the page isnt rendered, and the status bar doesnt change. You may use a setTimeout recursively:
function animateProgressBar(numIteration,index=1) {
width = (index/numIteration)*100;
htmlElement.style.width = width + '%';
if(index<numIteration) setTimeout(animateProgressBar,100,numIteration,index+1);
}
Start like this:
animateProgressBar(100); // from 1 to 100
Note that index=1 in the parameters is ES6, so just use it on modern browsers...
Related
So I'm working on a command line interface in the web. Currently, there is the start of a progress bar on the left of the CLI, and a few commands that work. I want to add more commands, but I felt that they needed to have a time delay feature to make it feel more authentic.
I want to make a function that, when called, will divide the (ms) used in the scenario by 12 (the number of dashes in the progress bar) and display an update every (ms).
The Progress bar looks something like this: [------------]
https://codepen.io/ZacV/pen/abEYpLz
function statsBar(ms){
var timeChunk = Math.round(ms/12)
for (let i = 0; i < timeChunk; i++) {
document.getElementById("DispStatus").innerHTML = "[" + ("|" * timeChunk) + ("-" * (12 - timeChunk)) + "]"
sleep(timeChunk);
}
}
Ok. After a bit of trial and error, I found the most efficient way to finish Rocky Sims snippet. Rocky Sims' code created a really nice and efficient progress bar, however the size of the bar was inconsistent. This final product includes a progress bar with a consistent width to match with the width of the div. Thanks for all of your guys' help on this!
function statsBar(ms){
var timeChunk = Math.round(ms/12);
for (let i = 0; i <= 12; i++) {
setTimeout(() => {
document.getElementById("DispStatus").innerHTML = "[" + ('|'.repeat(i)) + ('-'.repeat(12 - i)) + "]";
}, i * timeChunk);
}
}
statsBar(6000);
<div id="DispStatus"></div>
You can use setTimeout to schedule updates to the progress bar all at once but have them happen over time.
function statsBar(ms){
var timeChunk = Math.round(ms/12);
for (let i = 0; i < 12; i++) {
setTimeout(() => {
document.getElementById("DispStatus").innerHTML = "[" + ('-'.repeat(i)) + "]";
}, i * timeChunk);
}
}
statsBar(6000);
<div id="DispStatus"></div>
I want to create simple bar chart with animated bars (width from 0 to final size).
I started from css transitions but opera and chrome sometimes have problems.
Now I try to use mechanism what I saw here:
https://www.w3schools.com/w3css/w3css_progressbar.asp
using JS.
I have few "bar-areas":
<div id="chart-bars">
<div class="chart-bar-area">
<div class="chart-bar" data-percent="80" ></div>
</div>
<div class="chart-bar-area">
<div class="chart-bar" data-percent="60" ></div>
</div>
</div>
and JS code which works fine with one bar, but if I try to implement that mechanism for all in loop this script works correctly only for last one bar. Other bars not grooving.
My JS code below:
var foo = document.getElementById('chart-bars');
var width;
var elem;
var id;
for (var i = 0; i < foo.children.length; i++) {
elem = foo.children[i].children[0];
width = 1;
id = setInterval(frame, 10);
function frame() {
if (width >= elem.dataset.percent) {
clearInterval(id);
} else {
width++;
elem.style.width = width + '%';
}
}
}
Can somebody help me ?
Thanks
This is because value of i is out of scope of setInterval callback, setInterval takes only last value of iteration as it is a window method. So, you have to go with the closure or solution like this.
var foo = document.querySelectorAll(".chart-bar");
var width;
var elem;
var id;
for (var i = 0; i < foo.length; i++) {
var myElem = {
x: i
}
width = 1;
id = setInterval(function() {
if (width >= foo[this.x].dataset.percent) {
clearInterval(id);
} else {
width++;
foo[this.x].style.width = width + '%';
}
}.bind(myElem), 10);//Here you are binding that setInterval should run on the myElem object context not window context
}
Congratulations! Today will learn what a "closure" is.
You are experiencing a scoping issue. The reason this only works for the last bar is because the frame function only sees elem as it currently exists on scope. That is to say, by the time your setInterval runs your loop will have ended and elem will be firmly set to what amounts to foo.children[foo.children.length - 1].children[0]
The way to fix this is by creating a new closure. That is to say, you "close" the variable in a scope.
var foo = document.getElementById('chart-bars');
for (var i = 0; i < foo.children.length; i++) {
(function (elem, width) {
var id = setInterval(frame, 10);
function frame() {
if (width >= elem.dataset.percent) {
clearInterval(id);
} else {
width++;
elem.style.width = width + '%';
}
}
})(foo.children[i].children[0], 1)
}
now this, specifically, is a far from perfect way of accomplishing your goal, but I wrote it this way in order to preserve as much of your code as possible in an attempt to reduce the cognitive load for your specific example.
Essentially what I am doing is wrapping your code in an IIFE (Immediately Invoked Function Expression), which creates a new scope with your elem and width variables fixed to that function's scope.
A further step in the right direction would be to pull your frame function outside of the loop and have it only created once and have it accept as its arguments an elem, width, and id parameter, but I'll leave that as an exercise for the reader :-)
why don't you use the specific jquery lybraries to create the charts like morrischart or chartjs, there are very simple and they work very well
there are the documentations
http://morrisjs.github.io/morris.js/
https://www.chartjs.org
Looks like a variable scope issue, could you try something like this :
var foo = document.getElementById('chart-bars');
for (var i = 0; i < foo.children.length; i++) {
var elem = foo.children[i].children[0];
elem.style.width = '1%';
var id = setInterval(function() { frame(elem, id) }, 10);
}
function frame(elem, idInterval) {
var width = parseInt(elem.style.width);
if (width >= elem.dataset.percent) {
clearInterval(idInterval);
} else {
width++;
elem.style.width = width + '%';
}
}
Basically before you were erasing at each loop your previous elem, width, and id variables because they were declared outside the for loop. Hence the weird behavior.
Edit : putting the frame function outside so that it's clearer.
Edit2 : removing width from frame function as it is not needed.
To give a brief overview I am creating a loading bar that needs to go from 0 - 100 based on the total num of pins dropped onto a map in comparison to how many possible locations there are. So bellow I have some logic as to how I am going about achieving that.
//pin drop counter
var pDrop = 2;
//secondary drop counter
var dropCounter = pDrop
//result length counter
var rLength;
//pin drops and then runs the check marker count function
function dropPin(){ checkMarkerCount() }
//if a new pin was dropped then we increase the size of the bar
function checkMarkerCount() {
if (dropCounter != pDrop) {
moveBar();
}
function moveBar() {
//selects the loading bar from the DOM
var barEl = document.getElementById("pro-bar");
//states how wide the bar is
var barW = 1;
//dictates how fast it moves
var barid = setInterval(frame, 10);
//gets how many pins have dropped
var counter = pDrop;
function frame(counter) {
//rLength is the length of an array = the total amount of possible pin drops
if (counter >= rLength) {
clearInterval(barid);
} else {
barW = counter;
barEl.style.width = barW + '%';
console.log(barW)
}
}
}
}
The issues is that even if I say that pDrop is equal to 2 it will log that the length is undefined.... What have I done wrong?
You have to understand the concept of variables and arguments.
function frame(i_counter) {
//rLength is the length of an array = the total amount of possible pin drops
if (i_counter >= rLength) {
clearInterval(barid);
} else {
barW = i_counter;
barEl.style.width = barW + '%';
console.log(barW)
}
}
When you call the function frame, you should set params i_counter.
You think counter you passed as argument is the same you set above.
And barW = counter on your code take the argument you set.
I changed your code a bit, so you don't get rid of counter argument.
When executing frame, do this:
setInterval(function () {
frame(2);
}, 10);
My code is:
function slide(x)
{
if (x==undefined)
var x = 1;
if (x >= 4096)
return;
document.getElementById("slide").style.backgroundPosition = x + "px 0px";
x++;
setTimeout(function() {
slide(x);
}, 1);
}
JSFIDDLE
It makes a spin (?) by changing backgroundPosition, and it works. But it's too slow, I'd want to make it faster, and then gradually slow down. How can I do that?
You should pick a higher delay than 1ms. In most browsers 10 to 50 ms would be a lot more reliable. To speed up your animation though, increase x increments. For example:
function slide(x)
{
if(x==undefined) var x = 1;
if(x >= 4096) return;
document.getElementById("slide").style.backgroundPosition = x+"px 0px";
x += 10; // or some other value
setTimeout(function() {
slide(x);
}, 50); // or some other value
}
Also, you probably want to check x like this:
if (typeof x === 'undefined') { x = 1; }, no need for var.
2018 UPDATE:
Check out the https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame API. Using this over a fixed update interval is usually preferable.
I have rewrite all the function:
function slide() {
var x = 1;
var y = 30;
var clr = setInterval(function(){
if(x >= 4096) x = 1;
document.getElementById("slide").style.backgroundPosition = x+"px 0px";
x+=y;
y-=0.1;
if (y<=0) { clearInterval(clr); }
},10);
}
https://jsfiddle.net/tatrwkmh/4/
Currently the position is being changed by 1 pixel every time slide is called, via the line x++;. You can make it faster by changing this from x++ to x += 2 or x += 3 etc.
Your animation may look clunky without some sort of easing function, though. You should look into using some sort of animation library instead.
I got it nicely starting fast and then going slower by adding to your code the following:
if(x < 1000)
x+=2
else if(x < 1500)
x+=1.5
else
x++;
I have the current JavaScript problem. I have four divisions next to each other on my website that constantly rotate images on a 10 seconds interval. I need these intervals to keep rotating images at the current interval but start 5 seconds apart from each other in order to obtain a nice waterfall effect. How can I accomplish this using JavaScript?
image of how it looks on my websites' header
This is an example of the code I am currently using to display a single division and handle the rotation of the images.
<div class = "TestRotator">
<img src="http://bushveld.info/wp-content/uploads/2013/07/image1.png" alt="rotating" width="100" height="232" id="rotator">
<script type="text/javascript">
(function () {
var rotator = document.getElementById('rotator'); // change to match image ID
var imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/';
var delayInSeconds = 5;
// set number of seconds delay
// list image names
var images = ['image2.png', 'image3.png', 'image4.png'];
var num = 0;
var changeImage = function () {
var len = images.length;
rotator.src = imageDir + images[num++];
if (num == len) {
num = 0;
}
};
setInterval(changeImage, delayInSeconds * 1000);
})();
</script>
</div>
I've fiddled it a lot! (I changed it big time.)
chenged setInterval() with setTimeout() and many others.
Is this what you wanted?
PS: state holds the 1st image to which the imgs change. and the difference in the timeout (200 milliseconds is in order to just to make some difference in between them, yuo can change it to a round number if you want to).
If I've understood your question correctly, you need something like this:
window.onload = function () {
var // Customable parameters
imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/',
interval = 2, // Interval between "flushes" in seconds. Must be > speed * maxScreens
speed = 0.1, // "Flush" speed in seconds
maxScreens = 4, // amount of used image tags
images = 4, // amount of image sources, can be > maxScreens
dir = 1, // 1 = from left to right, -1 = from right to left
// Program
flush,
target = (dir > 0) ? 1 : maxScreens,
targetOrigo = target,
changeImage = function() {
var img = document.getElementById('rotator' + target),
id = parseInt(img.src.substr(img.src.length - 5, 1), 10) - dir;
if (id < 1) {
id = images;
}
if (id > images) {
id = 1;
}
img.src = imageDir + 'image' + id + '.png';
if (target !== maxScreens - targetOrigo + 1) {
target += dir;
setTimeout(changeImage, speed * 1000);
} else {
target = targetOrigo;
setTimeout(runRotation, interval * 1000);
}
return;
},
runRotation = function () {
setTimeout(changeImage, speed * 1000);
};
setTimeout(runRotation, 1000);
}
A live demo at jsFiddle
Notice, that I've put the function at window.onload, looks better when all the images are already loaded, before the rotation starts.
The snippet doesn't use setInterval() at all, instead it's based on nested setTimeout()s. This way you can avoid a mess, which you might get (depends on used browser), if user visits at other tab and then comes back to your site.
You can play with interval, "flush" speed, number of images you have on the rotation and even how many different images you like to use (max. = 9). You can also switch the direction of the rotation.
If you want to avoid two similar images to be shown at the same time, you can add image5.png to your image folder, and set images = 5.
Also version using an image source array available.
Thanx alot for the input. I solved this issue by adapting the code in this manner...
(function() {
var rotator3 = document.getElementById('rotator3'); // change to match image ID
var imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/';
// set number of seconds delay
// list image names
var images = ['image2.png', 'image3.png', 'image4.png', 'image1.png'];
// don't change below this line
var num = 0;
var changeImage = function()
{
var len = images.length;
rotator3.src = imageDir + images[num++];
if (num == len)
{
num = 0;
}
};
function SwImg() {
var rotate = setInterval(changeImage, 20000);
}
setTimeout(SwImg,15000);
})();
This tweak basically creates an initial delay of 5++ seconds at each division with the standard 20 seconds delay interval between switches, rotating each image in each division 5 seconds after the other. Here is a link to the website , will be done end of this week. Thanks again for the input, really awesome and creative ways of solving this issue!
Cheers