我创建了一个不在多个浏览器上同步的计时器。在浏览器的非活动选项卡上甚至在设备上,计时器运行得更快或更慢。 不同的标签上有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;
}
});
}
}
请在两个不同的标签上同时运行此网址,您会在几秒钟或1分钟内注明差异。 https://angular-timer-raf.stackblitz.io/
如果您愿意,也可以通过打开此网址==&gt;
来修改这些代码行网址https://stackblitz.com/edit/angular-timer-raf
我也知道当浏览器变为非活动状态然后计时器变慢时,我搜索了许多东西,但没有一个对我有效。但我需要以任何方式同步计时器。
答案 0 :(得分:1)
你做了一个外部(我希望是全局或拍卖特定的)服务来获得剩余的时间,你从该服务中获得了一个可观察的服务。好,我也会这样做!但我认为您应该从服务中删除“RequestAnimationFrame”。我的猜测是你用它来更新视图。你不应该这样做。 RequestAnimationFrame,就像“setTimeout()”一样,依赖于JS引擎在某个时候异步调用该方法。在此之前,线程可能会被某些视图层操作阻止。
所以你应该让Angular自己处理View-updates,例如:<div>Time: {{timer.numbers | async}}</div>
(异步管道会自动触发每个新值的changedetection)。
如果这没有帮助(不够精确),您可以考虑在完全不同的线程(浏览器中的WebWorker)上移动计时器服务
您还可以使用后端每隔......秒同步一次RAFTimer-Service。使用REST,您可以通过每隔......秒请求当前时间和其他拍卖信息来执行此操作;我假设您正在制作拍卖/竞标应用。使用websockets,您可以通过使后端服务在每次更新/间隔时将该信息推送到客户端来实现。
答案 1 :(得分:0)
我终于找到了解决方案。这是最终代码,它将在所有浏览器中同步计时器。
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(' ');
}
}