实现秒表的最有效方法是什么?

4

我希望能够记录用户点击按钮所需的时间。我已经解决了这个问题,但如果有更好的解决方案,我想尝试一下。以下是我的解决方法:

export class MainComponent implements OnInit {

    timer : number = 0;
    intervalId : number;

    constructor() {
      this.intervalId = setInterval(() => {
        this.timer++;
      }, 1000);
    }

    ngOnInit() {}

    buttonClick = function() {
    alert(this.timer);
    this.timer = 0;
    }
}

1
“setInterval” 完全不准确 - 会有相当大的抖动。只需获取当前系统时间并计算与开始时间的差异即可。 - Dai
可能是如何在JavaScript中创建准确的计时器?的重复问题。 - Dai
3个回答

3
使用performance.now()获取准确的时间戳(或回退到new Date().getTime()),并在UI更新回调函数(通过setInterval)中计算差异。不要使用setInterval本身来计算时间 - 你不能假设setInterval调用将精确地每1000毫秒调用一次。
请注意,我还将计时器逻辑移动到ngOnInit函数中,而不是constructor中。
export class MainComponent implements OnInit {

    private start: number = null;
    private uiTimerId: number = null;

    constructor() {
    }

    private updateUI(): void {

        let delta = performance.now() - this.start;
        this.someUIElement.textContent = delta.toFixed() + "ms";
    }

    ngOnInit() {

        this.start = parseFloat( window.localStorage.getItem( "timerStart" ) );
        if( !this.start ) {
            this.start = performance.now();
            window.localStorage.setItem( "timerStart", this.start );
        }

        this.uiTimerId = window.setInterval( this.updateUI.bind(this), 100 ); // 100ms UI updates, not 1000ms to reduce UI jitter
    }

    buttonClick = function() {
        if( this.uiTimerId != null ) {
            window.clearInterval( this.uiTimerId );
            window.localStorage.removeItem( "timerStart" );
        }
    }
}

如果用户刷新页面会怎样?我希望计时器能够继续而不是重置。 - Emenpy
@Emenpy,我修改了我的答案,演示了如何使用localStorage来保存start的值。 - Dai
为什么要使用间隔呢? - Zachscs
@Zachscs 要更新UI以显示秒表的时间值变化 - 它只是没有被用来通过将间隔相加来实际计算时间(这是完全错误的)。 - Dai
更新用户界面?你从哪里得到这个需求的?他的代码中只有一个警报,当按钮被点击时显示值。 - Zachscs
显示剩余4条评论

1

首先,在 TypeScript 中,我们的成员函数声明方式略有不同,因此 buttonClick 应该像这样:

buttonClick() {
  alert(this.timer);
  this.timer = 0;
}

如@Dai的评论中所述,获取系统时间(在ngOnInit时)并从单击时的系统时间中减去将需要更少的操作并且更准确。

ngOnInit() {
  this.startTime = localStorage.startTime ? JSON.parse(localStorage.startTime) : (new Date().getTime());
  localStorage.setItem('startTime', JSON.stringify(this.startTime));
}

buttonClick() {
  this.startTime = JSON.parse(localStorage.startTime);
  alert((this.startTime - (new Date().getTime())) / 1000);
}

编辑:我修改了答案,以向您展示如何使用localStorage来持久化值。这与上面的答案类似,但使用了惯用的typescript。我想之前的答案有很多es5的经验,而且正在诉诸这些方法(这没有任何问题)。我发现这种风格更容易理解和清晰。我建议您参加一个Angular教程。在他们的网站上尝试英雄之旅,并使用带有Angular Essentials插件的Visual Studio代码,这样它将适当地进行Lint和格式化,以便您可以习惯惯用的typescript。干杯。


如果用户刷新页面怎么办?我希望计时器继续运行而不是重置计时器。 - Emenpy
将开始时间保存在本地存储中。 - Zachscs
你能给我展示一下吗?我对这个非常新。另外,我该如何停止计时器和恢复计时器? - Emenpy
好的,请检查一下,我认为这会更加简单明了。 - Zachscs
感谢你对学习Angular的建议。我会去了解一下! - Emenpy
您的解决方案不起作用。刷新后,计时器会被重置。 - Emenpy

0

你可以按照以下方式编写代码,而不是使用增量计时器。当标签处于非活动状态时,计时器的增量将会改变。我以最高效的方式创建了一个秒表。你可以在不同的标签之间切换,时间计数值不会改变。你可以重置、暂停和播放。详情请访问链接。

  seconds: string = "00";
  minutes: string = "00";
  hours: string = "00";
   timer(){
    let mcountercal = 0;
    let currentSeconds = parseInt(this.seconds);
    let currentMinutes = parseInt(this.minutes);
    let currentHours = parseInt(this.hours);
    this.counter = currentHours * 3600000 + currentMinutes * 60000 + currentSeconds * 1000
    const startTime = Date.now() - (this.counter || 0);
    this.timeoutId = setInterval(() => {
      this.counter = Date.now() - startTime;
      currentHours = Math.floor(this.counter / 3600000);
      currentMinutes = Math.floor(this.counter / 60000) - currentHours * 60;
      mcountercal = Math.floor(this.counter / 60000);
      currentSeconds = Math.floor(this.counter / 1000) - mcountercal * 60;     
      this.hours = this.getFormattedTimeStamp(currentHours.toString());
      this.minutes = this.getFormattedTimeStamp(currentMinutes.toString())
      this.seconds = this.getFormattedTimeStamp(currentSeconds.toString())
}

getFormattedTimeStamp(timestamp:any) {
  return timestamp < 10 ? "0" + timestamp : timestamp;
}

https://stackblitz.com/github/Ashraf111/StopWatchWhenTabActiveAndInactive


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接