首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Javascript倒计时计时器不会在零停止,也不会在给定时间重新启动

Javascript倒计时计时器不会在零停止,也不会在给定时间重新启动
EN

Stack Overflow用户
提问于 2018-08-19 22:09:20
回答 3查看 1.5K关注 0票数 1

我的用例如下:

我需要我的倒计时时钟来查看本地机器时间,确定离上床时间还有多少时间,并在bt (上床时间)停止。

在UI上,它应该直观地显示00:00:00

但是,一旦本地机器时间与wt (唤醒时间)相同,它就应该重新开始倒计时,直到bt (睡眠时间)。

这应该一遍又一遍地重复。

另一个警告是,应用程序可能没有运行(即浏览器可能已关闭),并且脚本可能无法满足以下if条件:

if (hours === 0 && minutes === 0 && seconds === 0)

我该如何缓解这种情况呢?

我写了以下代码:

代码语言:javascript
复制
    $(document).ready(function () {
    
        var bt = "23:00";
        var dat = "10:00";
        var wt = "08:00";
    
        console.log('Bed Time:' + bt);
        console.log('Daily Available time' + dat);
        console.log('Wake up time:' + wt);
    
        placeHolderDate = "Aug 18, 2018 " + bt;
        var countDownDate = new Date(placeHolderDate).getTime();
    
    
        var countDownHourMin = (wt.split(":"));
    
    
    // Update the count down every 1 second
        var x = setInterval(function () {
    
            // Get todays date and time
            var now = new Date().getTime();
    
            // Find the distance between now and the count down date
            var distance = countDownDate - now;
    
            // Time calculations for days, hours, minutes and seconds
            var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
            var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
            var seconds = Math.floor((distance % (1000 * 60)) / 1000);
    
            $("#countDown").val(hours + "h " + minutes + "m " + seconds + "s ");
    
            // If the countdown is over, write some text
            if (hours === 0 && minutes === 0 && seconds === 0) {
                //clearInterval(x);
                $("#countDown").val("00:00:00");
            }
    
            if (hours < 0 || minutes < 0 || seconds < 0) {
               // clearInterval(x);
                $("#countDown").val("00:00:00");
            }
    
            console.log(hours + "h " + minutes + "m " + seconds + "s ");
    
        }, 1000);
    
    
    });
代码语言:javascript
复制
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="countDown"></p>

代码似乎可以工作,但也存在一些问题。倒计时进入负数(这可能是可以的,只要我可以简单地利用它来实现我的功能),一旦达到00:00:00wt (唤醒时间),时钟就不会重新启动。

EN

回答 3

Stack Overflow用户

发布于 2018-08-26 03:49:21

对date/time对象之间的差值进行模运算会给出时钟下一次显示该时间的日期/时间。

Modulo是除法的其余部分。让我们用一个24小时的简单例子来说明这一点:

现在是第二天的13:00。

一个警报是在第三天的15:00时。现在的差值是(15-13 + ( 3 -2)*24) = 26。26模24的结果是2,可以认为它是26 / 24 =1 rest 2

现在我们在第一天的12:00有一些警报日期,现在的差值是(12-13 + ( 1 -2)*24) = -25。-25模24的结果是23,因为24的下一个较低的倍数是-48,-25-(-48)是23。

不幸的是,JavaScript不支持开箱即用的真正的负数模运算。%运算符做了类似的事情,它导致无符号值的模运算。但是,您可以很容易地实现自己真正的模数方法:

代码语言:javascript
复制
  (dividend % divisor) + divisor) % divisor

实际上,我们不是以小时计算,而是以毫秒计算,因此我们将每天的毫秒作为除数。因此,在时钟显示Date对象中包含的时间部分之前,每天两个日期的模数毫秒之差就是毫秒数。我们将此时间与当前时间相加,得到下一个警报时间。这样我们就可以比较Date对象并计算与当前时间的差值。

此外,浏览器中的日期有几个问题。计时器功能不能准确工作。您可能会体验到使用setInterval()时的漂移。我在Firefox上测试了这一点,几毫秒后,每次都会触发inverval。这很快就累积成秒和分钟。

作为解决办法,我们可以使用setTimeout(),并根据当前时间计算下一整秒的触发时间,但是,回调可能会提前几毫秒触发。因此,我们不能依赖Date对象的getSeconds()。由于这一事实,我们需要实现一个近似值,它将舍入为满秒或1/100秒。

我们可以扩展Date对象的原型以提高可用性。

代码语言:javascript
复制
$(() =>
{
  const
    millisecondsPerDay = 1000*60*60*24,
    milliSecTolerance  = 0,        // timer functions in browser do not work exactly
    clockPrecision     = 10,       // timer functions in browser do not work exactly, round to 10 millisec.
    emptyTimeString    = new Date().toLocaleTimeString().replace(/\d/g, '-')   // e.g. '--:--:--';
  ;


  // Since JavaScript % operator does not work propperly on neg. numbers, we want a true Modulo operation.
  // 23 mod 10 = 3 (correct); -23 mod 10 = 7   (means 7 more than -30, %-op gives 3)
  // We could do that in a Number prototype method:
  Object.defineProperties(Number.prototype,
  {
    mod : { value: function(n) { return ((this%n)+n)%n; } }
  });


  function lowerPrecision(operand, precision)
  {
    if(void 0 === precision)
      precision = clockPrecision;
    let result = Math.round(operand.valueOf() / precision)*precision;
    return Date.prototype.isPrototypeOf(operand) ? new Date(result) : result;
  }

  // Let's extend the Date object to make it more handy
  Object.defineProperties(Date.prototype,
  {
    toUTCTimeHMS       : { value: function() { return this.toUTCString().match(/(.{8}) GMT/)[1];; }},

    setFormattedTime   : { value: function(timeString)
    {
      this.setHours(...timeString.split(/[:.]/));
      return this;  // support chaining
    }},

    getApproximateDate : { value: function(precision)
    {
      if(void 0 === precision)
        precision = clockPrecision;
      return lowerPrecision(this, precision);
    }},

    getApproximateTime : { value: function(precision) { return this.getApproximateDate().getTime(); } },

      // Returns the next date/time when the time component will be reached
    nextDailyTimeDate  : { get  : function()
    {
      let now = Date.getApproxNow();
      return new Date(now + (this-now).mod(millisecondsPerDay));
    }},
  });


  // Timers do not work accurately. The might execute even some milliseconds too early.
  // Let's define a custom functional now-property that gives an approximated value in steps of some milliseconds.
  Object.defineProperties(Date,
  {
    getApproxNow      : { value: (precision) => lowerPrecision(Date.now(), precision) },
    getDateApproxNow  : { value: (precision) => new Date().getApproximateDate(precision) },
  });


  // ===================================================================================


  var
    nextTick,
    alarms = []
  ;


  function Alarm(tr, collection)
  {
    let
      $tr                 = $(tr)            ,
      input               = $tr.find('td>input')[0],
      th                  = $tr.find('th'      )[0],
      tdRemaining         = $tr.find('td'      )[1]
    ;

    Object.defineProperties(this,
    {
      tr        : { get: () => tr          },
      th        : { get: () => th          },
      input     : { get: () => input       },
      remaining : { get: () => tdRemaining },
      collection: { get: () => collection  },
    });

    this.update();
    this.registerEvents();
  }


  // shared prototype doing all the stuff
  Alarm.prototype = new function()
  {
    Object.defineProperties(this,
    {
      update          : { value: function ()
      {
        this._nextDate = new Date().setFormattedTime(this.input.value).nextDailyTimeDate;
        this.collection.updateDisplay();
      }},

      nextDate        :
      {
        get: function() { return this._nextDate; },
        set: function(value)
        {
          let date;
          switch(Object.getPrototypeOf(value))
          {
            case Date:
              date = value;
              break;
            case String.prototype:
              date = new Date().setFormattedTime(value);
              break;
            case Number.prototype:
              date = new Date(value);
              break;
            default:
              return null;
          }
          this._nextDate = date.nextDailyTimeDate;
          this.input.value = this._nextDate.toLocaleTimeString();
        }
      },
      registerEvents  : { value: function() { $(this.tr).find('input').on('change', (ev) => { this.update(); }); }},
      valueOf         : { value: function() { return this._nextDate } },
      remainingTime   : { get  : function() { return new Date(this._nextDate.getApproximateTime()); } },
      updateDisplay   : { value: function()
      {
        this.remaining.innerText = this === this.collection.nextAlarm
          ? new Date(this.remainingTime - Date.getDateApproxNow()).toUTCTimeHMS()
          : emptyTimeString
        ;

        if(this._nextDate.getApproximateTime() > Date.getDateApproxNow())
          return;

        this.update();
        return true;
      }},
    });
  };


  Object.defineProperties(alarms,
  {
    updateDisplay : { value: function()
    {
      let changed = false;
      do for(let i in this)
        if(changed = this[i].updateDisplay())
          break;
      while(changed); // refresh display of all alarms when any data has changed while processing
    }},

    nextAlarm     : { get  : function()
    {
      return this.length
        ? this.reduce((acc, cur) => cur.nextDate<acc.nextDate ? cur:acc)
        : null
      ;
    }},
  });


  $('#alarm-table tr:nth-child(n+2)').each( (i, tr) =>alarms[i] = new Alarm( tr, alarms ) );


  function onTick()
  {
    alarms.updateDisplay();
  }


  (function tickAtFullSeconds()
  {
    onTick();
    nextTick = setTimeout(tickAtFullSeconds, milliSecTolerance + 1000 - new Date().getMilliseconds());
  })();




  $('#test-button').click((ev) =>
  {
    time = Date.now();
    alarms.forEach(i=>i.nextDate = (time += 5000));
  });
  window.alarms = alarms; //DEBUG global access from browser console
});
代码语言:javascript
复制
tr:nth-child(n+2)>th
{
  text-align: left;
  background-color: silver;
}
td
{
  background-color: lightgray;
}
th
{
  background-color: grey;
}
代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Alarm</title>

  <link rel="stylesheet" href="AlarmTimer.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>



<body>
  <h1> Alarm Timer </h1>
  <h2 id="message"></h2>
  <table id="alarm-table">
    <tr>
      <th>Alarm</th>
      <th>Time</th>
      <th>Remaining</th>
    </tr>
    <tr id="waking-up-time">
      <th>waking-up time</th>
      <td class="time"     ><input type="time" step="1" value="07:15:00"></td>
      <td class="remaining"> --:--:--</td>
    </tr>
    <tr id="noon-hour">
      <th>noon hour</th>
      <td class="time"     ><input type="time" step="1" value="12:00:00"></td>
      <td class="remaining"> --:--:--</td>
    </tr>
    <tr id="bed-time">
      <th>bed time</th>
      <td class="time"     ><input type="time" step="1" value="22:00:00"></td>
      <td class="remaining"> --:--:--</td>
    </tr>
  </table>

  <button id="test-button">set test times</button>

</body>
</html>

票数 1
EN

Stack Overflow用户

发布于 2020-10-15 02:42:39

这是我的代码。它开始倒计时,然后在秒数为零时停止。

代码语言:javascript
复制
import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
    const [ minutes, setMinutes ] = useState(0);
    const [ seconds, setSeconds ] = useState(0);

    let userMints = 60;
    let time = userMints * 60;
    const showCountDown = () => {
        const minutes = Math.floor(time / 60);
        let seconds = time % 60;

        if (seconds < 0) {
            setMinutes(0);
            setSeconds(0);
            return;
        } else {
            setMinutes(minutes);
            setSeconds(seconds);
            time--;
        }
        return;
    };

    useEffect(() => {
        callTimerAfterEverySec();
    }, []);

    const callTimerAfterEverySec = () => {
        setInterval(() => {
            showCountDown();
        }, 1000);
    };

    const renderStatus = () => {
        if (seconds === 0 && minutes === 0) {
            return (
                <div>
                    <p>Time Up</p>
                </div>
            );
        } else {
            return (
                <div>
                    <p>
                        {minutes}:{seconds}
                    </p>
                </div>
            );
        }
    };

    return (
        <div className="App">
            {renderStatus()}
            <p>{/* {minutes}:{seconds} */}</p>
        </div>
    );
}

export default App;
票数 1
EN

Stack Overflow用户

发布于 2018-08-20 05:27:17

代码语言:javascript
复制
$(document).ready(function () {

  function countdown() {
     var bt = "23:00",  // 11:00 PM
         wt = "08:00";  // 08:00 AM


    var today = new Date(),
        dd = today.getDate(),
        mm = today.getMonth()+1,
        yyyy = today.getFullYear();

    var startTime = new Date(mm + '/' + dd + '/' + yyyy + ' ' + wt),
        endTime = new Date(mm + '/' + dd + '/' + yyyy + ' ' + bt);

    setInterval(function() {
       var now = new Date();
       var nowdd = today.getDate();
       var nowTime = now.getTime();
      if(dd !== nowdd) {
        dd = nowdd;
        startTime = new Date(dd + '/' + mm + '/' + yyyy + ' wt');
        endTime = new Date(dd + '/' + mm + '/' + yyyy + ' bt');
      }

      if(nowTime > startTime && nowTime < endTime) {
         // Find the distance between now and the count down date
            var distance = endTime - nowTime;

            // Time calculations for days, hours, minutes and seconds
            var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)),
                minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)),
                seconds = Math.floor((distance % (1000 * 60)) / 1000);
            $("#countDown").val(hours + ":" + minutes + ":" + seconds);
         } else {
           $("#countDown").val("00:00:00");
         }
    }, 1000);
  }
  countdown();
});

CodePen上。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51918480

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档