Таймер не синхронизируется при работе на нескольких браузерах или устройствах - PullRequest
0 голосов
/ 05 мая 2018

Я создал таймер, который не синхронизируется в нескольких браузерах. На неактивных вкладках браузеров или даже на устройствах таймер работает быстрее или медленнее. На разных вкладках разница 3-4 секунды. Как их синхронизировать?

Это мой код модуля:

import { Observable, BehaviorSubject, Subscription } from 'rxjs';
export class RAFTimer {
    public currentFrameId: number;
    public startTime: number;
    public numbers: BehaviorSubject<number>;
    constructor() {
        this._raf();
        this.numbers = new BehaviorSubject<number>(0);
        this.startTime = 0;
    }

    private _raf() {
        this.currentFrameId = requestAnimationFrame(timestamp => this._nextFrame(timestamp));
      }
      private _nextFrame(timestamp: number) {
        if ( this.startTime == 0) {
            this.startTime = timestamp;
        }
        const diff: number = timestamp - this.startTime;
        if ( diff > 100) {
           const n: number = Math.round(diff / 100);
          this.startTime = timestamp;
          this.numbers.next(n);
        }
        this._raf();
      }
      cancel() {
        cancelAnimationFrame(this.currentFrameId);
        this.currentFrameId = null;
      }
}

Это код компонента:

import { Component, Input, OnInit, OnDestroy } from '@angular/core';
import { RAFTimer } from './raftimer';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
  title = 'app';
  public timeInterval:number=10;
  public timeLeft:number=10000;
  constructor(private timer:RAFTimer) { 

  }

  ngOnInit() {
    let countNumber=1;
    let auctiontimer = this.timer.numbers.subscribe(
      (val) => {
          countNumber = countNumber+val;
          if(countNumber >= this.timeInterval) {
            this.timeLeft = this.timeLeft-countNumber/this.timeInterval;
            if(this.timeLeft<0) {
                this.timeLeft= 0;
            }
              countNumber=1;
          }
  }); 
  }

}

Пожалуйста, запустите этот URL на двух разных вкладках вместе, и вы заметите разницу через несколько секунд или в течение 1 минуты. https://angular -timer-raf.stackblitz.io /

Если вы хотите, вы можете также отредактировать эти строки кода, открыв этот URL ==>

URL https://stackblitz.com/edit/angular-timer-raf

Я также знаю, что когда браузер становится неактивным, таймер замедляется, я много раз искал, но у меня ничего не работает. Но мне нужно как-то синхронизировать таймер.

Ответы [ 2 ]

0 голосов
/ 21 июня 2018

Я наконец-то получил решение. Это последний код, который будет синхронизировать таймер во всех браузерах.

import { Component, OnInit, Input } from '@angular/core';
import { Observable, Subscription } from 'rxjs/Rx';

@Component({
  selector: 'app-timer',
  templateUrl: './timer.component.html',
  styleUrls: ['./timer.component.css']
})
export class TimerComponent implements OnInit {
  @Input() futureDateTime: string;
  public diff: number;
  public timeLeft: string;
  private $counter: Observable<number>;
  private subscription: Subscription;
  constructor() { }

  ngOnInit() {
    this.countdown();
  }
  countdown() {
    // endDateTime YYYY-mm-dd H:m:s
    const endDateTime = new Date(this.futureDateTime);
    let currentDate = new Date().getTime();
    //  Get difference in milliseconds between current date and future date ...
    let diffInMillSeconds = this.getDiffInMillSeconds(currentDate, endDateTime);
    diffInMillSeconds = diffInMillSeconds + 1000;
    this.$counter = Observable.interval(1000).map((x) => {
      currentDate = new Date().getTime();
      this.diff = Math.floor((endDateTime.getTime() - currentDate) / 1000);
      return x;
    }).takeUntil(Observable.timer(diffInMillSeconds));
    this.subscription = this.$counter.subscribe((x) => {
      if (this.diff === 0) {
        this.timeLeft = '0.0.0';
      } else if (this.diff > 0) {
        this.timeLeft = this.dhms(this.diff);
      }
    });
  }
  /**
   * @desc Function to get milli seconds between current date and future date
   */
  getDiffInMillSeconds(currentDate, futureDate) {
    const dif = currentDate - futureDate.getTime();
    const Seconds_from_T1_to_T2 = dif / 1000;
    const Seconds_Between_Dates = Math.abs(Seconds_from_T1_to_T2);
    const milliSeconds = Seconds_Between_Dates * 1000;
    return milliSeconds;
  }
  /**
   * @desc Function to calculate day, hours, minutes and seconds..
   */
  dhms(t) {
    let days, hours, minutes, seconds;
    days = Math.floor(t / 86400);
    t -= days * 86400;
    hours = Math.floor(t / 3600) % 24;
    t -= hours * 3600;
    minutes = Math.floor(t / 60) % 60;
    t -= minutes * 60;
    seconds = t % 60;
    return [
      // days + 'd',
      hours + '.',
      minutes + '.',
      seconds
    ].join(' ');
  }
}
0 голосов
/ 05 мая 2018

Вы сделали внешний (я надеюсь, глобальный или специфичный для аукциона) сервис, чтобы получить оставшееся время, и вы получаете наблюдаемый сервис. Хорошо, я бы сделал то же самое! Но я думаю, что вы должны удалить этот «RequestAnimationFrame» из службы. Я предполагаю, что вы используете его для обновления представления. Вы не должны этого делать. RequestAnimationFrame, как и setTimeout (), - это метод, который использует JS-движок для вызова этого метода асинхронно в некоторый момент времени. И до того, как это произойдет, поток может быть заблокирован некоторыми операциями уровня представления.

Таким образом, вместо этого вы должны позволить самому Angular обрабатывать обновления View, например, вот так: <div>Time: {{timer.numbers | async}}</div> (асинхронный канал автоматически запускает определение изменений при каждом новом значении).

Если это не помогает (не достаточно точно), вы можете рассмотреть возможность перемещения службы таймера в совершенно другой поток (WebWorker в браузере)

Вы также можете использовать бэкэнд для синхронизации вашего RAFTimer-Service каждые ... секунды. С REST вы можете сделать это, отправляя запрос каждые ... секунды, запрашивая текущее время и другую информацию об аукционе; Я предполагаю, что вы делаете приложение аукциона / торгов. С помощью веб-сокетов вы могли бы сделать это, заставляя серверную службу передавать эту информацию клиенту при каждом обновлении / интервале.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...