import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, NgZone, OnDestroy, Output, ViewChild } from '@angular/core'; @Component({ selector: 'app-__cp__-countup', template: '
', styles: [ ` div { display: inline-block; font-variant-numeric: slahed-zero; } ` ], changeDetection: ChangeDetectionStrategy.OnPush }) export class __capcp__CountupComponent implements OnDestroy, AfterViewInit { @Input() startTimeInMillis: number; @Input() diffTimeInMillis = -1; @Output() timeout = new EventEmitter(); @ViewChild('cmp', {static: true}) private readonly div: ElementRef; private alive = true; constructor(private readonly ngZone: NgZone, private readonly cdr: ChangeDetectorRef) { this.cdr.detach(); } ngOnDestroy(): void { this.alive = false; } ngAfterViewInit(): void { this.startTimeInMillis = Date.now(); this.ngZone.runOutsideAngular(() => { // Der erste Aufruf (, später rekursiv): requestAnimationFrame(this.updateText.bind(this)); }); } private updateText(): void { if (!this.alive) { return; // skip } const now = Date.now(); const spentInMillis = now - this.startTimeInMillis; const seconds = this.pad((spentInMillis / 1000) % 60); const minutes = this.pad((spentInMillis / 1000 / 60) % 60); // const hours = this.pad((total / (1000 * 60 * 60)) % 24); // const days = this.pad(total / (1000 * 60 * 60 * 24)); // update GUI: this.div.nativeElement.innerText = `${minutes}:${seconds}`; if (spentInMillis < this.diffTimeInMillis || this.diffTimeInMillis === -1) { setTimeout(() => { requestAnimationFrame(this.updateText.bind(this)); }, 900); } else { this.ngZone.run(() => this.timeout.emit(now)); } } private pad(value) { const n = Math.floor(value); if (n < 10) { return '0' + n; } return n; } }