EvanW
EvanW

Reputation: 71

Angular Material 2: Progress Bar animation choppy

I'm trying to build a page with Angular that has various progress bars that represent the progress made for an action that takes a set amount of time. What I'm trying to do is use the Angular Material progress bar to make a smooth animation from time 0 to the end time. My current solution is simply to update the percentage of the process every 50ms, and set the value of the progress bar to that value. I'm accomplishing this using the rxjs timer, as seen below:

const start = new Date().getTime();
const source = timer(0, 50);
const subscribe = source.subscribe(() => {
  const time = new Date().getTime();
  if (time - start > length) {
    subscribe.unsubscribe();
  }

  this.progressValue = 100 * (time - start) / length;
});

Here is the progress bar HTML itself:

 <mat-progress-bar mode="determinant" [value]="progressValue"></mat-progress-bar>

The result, however, is a very choppy progression of the progress bar. Is there a better way to do this? I know the length of the process every time, so I feel that a smooth transition for that amount of time should be relatively easy. Thanks!

Upvotes: 7

Views: 5003

Answers (3)

Based on Nodarii answer but simpler :

HTML:

<mat-progress-bar color="accent" mode="determinate" [value]="uploadPercentage$ | async"></mat-progress-bar>

Component:

@ViewChild(MatProgressBar) progressBar: MatProgressBar;
uploadPercentage$: Observable<number>;

  ngAfterViewInit(): void {
    this.uploadPercentage$ = this.progressBar.animationEnd.pipe(
      startWith(0),
      delay(0),
      map(() => this.yourPercentage)
    );
  }

Angular Material do the animation ;)

Upvotes: 0

I've created my own progress bar with Angular Animations. This has a smooth transition and looks just like mat-progress-bar:

1) add Animations to your component:

  selector: 'app-loading',
  templateUrl: './loading.component.html',
  styleUrls: ['./loading.component.scss'],
  animations: [
    trigger('loading', [
      state('start',
        style({
          width: '0%'
        })
      ),
      state('end',
        style({
          width: '100%'
        })),

      transition('start => end', [
        animate(2000)
      ]),
    ])
  ]
})

2) set a trigger timer to start animation. I close the dialog after progress ends:

duration: number = 2 * 100;
step = 100;
add_step = 10;
progress = 0;


ngOnInit() {
    const interval = setInterval(() => {
      if (this.progress < this.duration) {
        this.progress += this.add_step ;
      } else {
        clearInterval(interval);
        this.dialogRef.close();
      }
    }, this.step);
  }

3) use it in Html:

<div style="background-color: #ff7a7a;height: 4px;width: 100%;">
        <div style="background-color: #760000;height: 4px;"
             [@loading]="progress === 0 ? 'start':'end'">
        </div>
</div>

Upvotes: 0

Nodarii
Nodarii

Reputation: 984

here is what I came up with:

html

<mat-progress-bar color="accent" mode="determinate" [value]="uploadPercentage$ | async"></mat-progress-bar>

component:

@ViewChild(MatProgressBar) progressBar: MatProgressBar;
uploadPercentage$: Observable<number>;
uploadProgress$: Subject<HttpProgressEvent> = new Subject<HttpProgressEvent>();

ngAfterViewInit(): void {
    // wait until animation ends and only then change value
    this.uploadPercentage$ = this.progressBar.animationEnd
      .pipe(
        startWith({
          value: 0
        }),
        switchMap((animationState: ProgressAnimationEnd) => this.uploadProgress$
          .pipe(
            map((progress: HttpProgressEvent): number => this.progressToPercents(progress)),
            distinctUntilChanged((val: number) => animationState.value !== val)
          )
        ),
      );
  }

Upvotes: 1

Related Questions