0

我正在用 PHP 创建一个定时测试产品。我在正确控制考试时间方面遇到了问题。当考试开始时,只要我在同一个屏幕上,计时器就会正常工作。但是,当我在测试进行时(比如 10 分钟)打开其他屏幕,并且在一段时间后返回测试时,计时器显示错误的时间,并且显示好像只有前 10 分钟消耗的测试时间的 2 分钟。

只是想知道为什么当我在测试开始时离开屏幕并在一段时间后回来时测试计时器的这种不稳定行为。

谁能帮我解决这个问题?

PHP代码

<?php
/*Timer Code*/
        $dateFormat = "d F Y -- g:i a";
        $targetDate = time() + (240*60);//Change the 240 to however many minutes you want to countdown
        $actualDate = time();
        $secondsDiff = $targetDate - $actualDate;
        $remainingDay     = floor($secondsDiff/60/60/24);
        $remainingHour    = floor(($secondsDiff-($remainingDay*60*60*24))/60/60);
        $remainingMinutes = floor(($secondsDiff-($remainingDay*60*60*24)-($remainingHour*60*60))/60);
        $remainingSeconds = floor(($secondsDiff-($remainingDay*60*60*24)-($remainingHour*60*60))-($remainingMinutes*60));
        $actualDateDisplay = date($dateFormat,$actualDate);
        $targetDateDisplay = date($dateFormat,$targetDate);
?>

JavaScript

/*Timer Code*/
  var days = <?php echo $remainingDay; ?>  
  var hours = <?php echo $remainingHour; ?>  
  var minutes = <?php echo $remainingMinutes; ?>  
  var seconds = <?php echo $remainingSeconds; ?> 

function setCountDown ()
{
  seconds--;
  if (seconds < 0){
      minutes--;
      seconds = 59
  }
  if (minutes < 0){
      hours--;
      minutes = 59
  }
  if (hours < 0){
      days--;
      hours = 23
  }
  document.getElementById("remain").innerHTML = "Time "+hours+" H : "+minutes+" M : "+seconds+" S";
  SD=window.setTimeout( "setCountDown()", 1000 );
  if (hours == '0' && minutes == '00' && seconds == '00') 
  { 
      seconds = "00"; window.clearTimeout(SD);
      timeStatus = 1;
      var response = window.alert("Time is up. Press OK to continue."); // change timeout message as required         
  } } // here is the end of setCountDown
4

3 回答 3

1

当 JavaScript 不在前台运行时,它不会总是处理计时器滴答声,这就是为什么你会变慢的原因。

如果您这样做会更可靠:

  • 在 JS 中使用目标截止日期,每次处理计时器事件时将其转换为剩余时间。这样,它将始终与实际截止日期相关。
  • 不时刷新服务器的截止日期信息(不是每秒一次,但可能每 5 分钟一次)。这意味着您必须在服务器端使用某种持久性,例如会话范围变量,该变量具有可以返回剩余秒数的截止日期。
  • 在服务器端验证提交的答案不会超过截止日期(如果有人篡改了客户端)。同样,您需要一些会话范围的使用来实现这一点。

我不会提供第二个或第三个想法的代码,但这里是适用于第一个想法的代码(这可能足以满足您的目的?):

/*Timer Code*/
var totalSeconds = 240*60; //<?php echo 240*60; ?>  
var deadlineMillis = Date.now() + totalSeconds * 1000;

function setCountDown () {
    var secondsRemaining = Math.max(0, Math.round((deadlineMillis - Date.now()) / 1000)),
        minutes = Math.floor(secondsRemaining / 60),
        hours = Math.floor(minutes / 60),
        days = Math.floor(hours / 24);
    hours = hours % 24;
    minutes = ('0' + (minutes % 60)).substr(-2);
    seconds = ('0' + (secondsRemaining % 60)).substr(-2);
    document.getElementById("remain").textContent = 
        "Time "+hours+" H : "+minutes+" M : "+seconds+" S";
    if (secondsRemaining === 0) { 
        timeStatus = 1;
        alert("Time is up. Press OK to continue."); // change timeout message as required
        return;
    }
    setTimeout(setCountDown, 1000);
}

setCountDown();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="remain"></div>

将 替换240*60为 PHP 生成的值。

于 2017-09-06T15:53:26.780 回答
1

这适用于我列出的更改:

PHP:

<?php
/*Timer Code*/
        $dateFormat = "d F Y -- g:i a";
        $targetDate = time() + (240*60);//Change the 240 to however many minutes you want to countdown
        $actualDate = time();
        $secondsDiff = $targetDate - $actualDate;
        $remainingDay     = floor($secondsDiff/60/60/24);
        $remainingHour    = floor(($secondsDiff-($remainingDay*60*60*24))/60/60);
        $remainingMinutes = floor(($secondsDiff-($remainingDay*60*60*24)-($remainingHour*60*60))/60);
        $remainingSeconds = floor(($secondsDiff-($remainingDay*60*60*24)-($remainingHour*60*60))-($remainingMinutes*60));
        $actualDateDisplay = date($dateFormat,$actualDate);
        $targetDateDisplay = date($dateFormat,$targetDate);
?>
  • PHP打开应该是

JavaScript:

/*Timer Code*/
  var days = 0;
  var hours = 4;
  var minutes = 0;
  var seconds = 0;

  setCountDown();

function setCountDown ()
{
  seconds--;
  if (seconds < 0){
      minutes--;
      seconds = 59
  }
  if (minutes < 0){
      hours--;
      minutes = 59
  }
  if (hours < 0){
      days--;
      hours = 23
  }
  if (hours == '0' && minutes == '00' && seconds == '00') 
  { 
      seconds = "00"; 
      window.clearTimeout(SD);
      timeStatus = 1;
      var response = window.alert("Time is up. Press OK to continue."); // change timeout message as required         
  } 
  console.log(days+'/'+hours+'/'+minutes+'/'+seconds);
  SD=setTimeout(setCountDown, 1000);
}
  • setCountDown 缺少它的右括号
  • 应该只使用函数的名称调用 setTimeout。在名称之后使用 () 调用它是将返回值传递给 setTimeout,而不是函数本身。(最后一点与您的问题无关,因为您在引号中调用了函数)
  • 在检查您是否处于 0 之后,应该真正重新调用该函数。

对于更准确的计时器系统,请考虑使用来自服务器和用户计算机的时间戳,而不是可以操纵的变量。然后可以不断的从用户的电脑中拉取当前时间,看看已经过去了多少时间。

于 2017-09-06T15:37:25.353 回答
0

大多数现代浏览器故意将非活动选项卡/窗口的优先级降低。因此计时器会变慢。所以你面临的不是古怪的行为。提高浏览器性能是设计使然。

https://developers.google.com/web/updates/2017/03/background_tabs#budget-based_background_timer_throttling

https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Timeouts_in_inactive_tabs_throttled_to_ >1000ms

解决方案:使用 web worker

于 2017-09-06T16:36:24.620 回答