Reputation: 469
im trying to add a progressbar but sometimes it bugs out and fills too slow or too fast, most of the time it works fine though.
I also think that there must be a better way to programm this since mine does way to many calculations for a single progressbar i think.
Here is my Code:
ts:
startProgressbar(timeToWait: number) {
this.progress = 0;
const interval = setInterval(() => {
let addvalue = (1 / timeToWait * 100) / 64;
this.progress += addvalue;
if (this.progress >= 100) {
this.progress = 100;
window.clearInterval(interval);
}
}, 15.625); }
The function gets called again as soon as its over since there will be the next slide of the slideshow directly after.
The progress value resembles the width of the progressbar, its always a percentage.
Where does the number 15.625 come from? Its simply a 64th of a second so the progressbar updates 64 times a second giving the illusion of moving smoothly.
My html is pretty basic:
<div class="progressbar" [ngStyle]="{'width': progress+'%'}"></div>
I hope you have a better approach than i did. Thanks in advance
[EDIT]: What i mean by it bugging out is that the progressbar sometimes starts filling late and therefor doesnt reach the end (100%) before the next slide appears and then the next ones timing is offset too. Sometimes it starts early and then reaches the end (100%) too quickly so it stays at 100% for a while until the next slide appears.
Upvotes: 3
Views: 2026
Reputation: 57939
why not use Rxjs timer?
interval(timeToWait):Observable<number>
{
let progress = 0;
return timer(0, timeToWait)
.pipe(
takeWhile(() => progress <= 100),
map(()=>progress<100?progress:100),
tap(() => (progress += 10))
)
}
Use
this.interval(200).subscribe((res) => {
console.log(res);
});
Update add stackblitz: stackblitz
Update 2 thanks to @Ritaj, timer return a stream (0,1,2,3,4,...), so we can use simple
return timer(0, timeToWait)
.pipe(
map((progress)=>progress*5<100?progress*5:100),
takeWhile((progress) => progress <= 100)
)
Update 3 thanks to this answer of SO, we can included the last value whe we are using takeWhile, so, if is important the timeToWait we can replace the function interval by
interval(timeToWait) {
const initial = new Date().getTime();
return timer(0, 200).pipe(
map(()=> new Date().getTime()),
takeWhile((res) => res<=initial+timeToWait,true),
map(now=>{
const porc = (100 * (now - initial)) / timeToWait;
return porc<100?Math.round(porc):100
})
);
Upvotes: 5
Reputation: 2250
I recommend following the Javascript best practice for waiting. Here is a stackblitz I've prepared with example code for a progress bar that uses the recommended practices.
export class AppComponent {
name = 'Angular';
public progress = 0;
public timeToWait: number = 1;
private default_ms = 8.33; // 120 times a second.
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
public async startProgressBar(): Promise<void> {
this.progress = 0;
while(this.progress <= 100) {
this.progress += (1 / this.timeToWait);
await this.sleep(this.default_ms);
}
}
}
I can't say for sure why your solution "bugs out". Calling window.clearInterval
inside the interval function seems like bad practice, but I can't be certain. Let me know if this solution works for you.
Upvotes: 1