定时器功能在多次停止和启动时无法正常工作



首先,您可以在JS Fiddle中找到我的代码示例,也可以在问题下面找到。

我正在开发一个个人训练网络应用程序,基本上你可以点击播放,然后你有五分钟的时间按随机顺序完成一系列任务。该程序创建sessionTasks数组,其中以随机顺序放置tasks数组的任务,以满足五分钟的限制。现在,tasks数组只是我创建的一个数组,它包含四个任务和相应的时间,仅用于测试。

我遇到的问题如下:当你点击任务以便前进到下一个任务时,下次玩秒会移动得更快。我发现复制的方法是:

  1. 单击播放
  2. 通过快速单击任务文本来完成任务
  3. 再次单击播放

现在秒应该移动得更快了。如果没有,请重复刚才所做的操作。它是不规则的,但通常在第二次尝试时就完成了。

我一辈子都不明白它为什么会这样。我想可能是创建了更多的定时器,都使用#taskTimer运行,但这对我来说没有意义。Timer函数有问题吗?我的代码出了什么问题?

mainMenu();
var totalSessionTasks, taskIterator, selectedTimeInSecs = 300;
var taskTimer = new Timer("#taskTimer", nextTask);
var globalTimer = new Timer("#globalTimer", function() {
});
var tasks = [
  ["First task", 0, 30],
  ["Second task", 0, 15],
  ["Third task", 0, 10],
  ["Fourth task", 3, 0]
];
var sessionTasks = [
]

function setUpSession() {
  sessionTasks = []
  if (tasks.length != 0) {
    var sessionTasksSeconds = 0; //the seconds of the session being filled
    var sessionTasksSecondsToFill = selectedTimeInSecs; //seconds left in the session to fill
    var newTaskSeconds = 0; //seconds of the next task being added to the session
    var sessionFull = false;
    console.log('Session Empty');
    while (sessionFull === false) {
      var areThereAnyTaskThatFitInTheSession =
        tasks.some(function(item) {
          return ((item[1] * 60 + item[2]) <= sessionTasksSecondsToFill) && (item != sessionTasks[sessionTasks.length - 1]);
        });
      console.log(areThereAnyTaskThatFitInTheSession);
      if (areThereAnyTaskThatFitInTheSession) {
        do {
          var randTaskNum = Math.floor(Math.random() * tasks.length);
        } while (((tasks[randTaskNum][1] * 60 + tasks[randTaskNum][2]) > sessionTasksSecondsToFill) || (tasks[randTaskNum] == sessionTasks[sessionTasks.length - 1]))
        sessionTasks.push(tasks[randTaskNum]);
        newTaskSeconds = (tasks[randTaskNum][1]) * 60 + tasks[randTaskNum][2];
        sessionTasksSecondsToFill -= newTaskSeconds;
        sessionTasksSeconds += newTaskSeconds;
        console.log(tasks[randTaskNum][0] + ": " + newTaskSeconds + "s");
        console.log(sessionTasksSeconds)
      } else if (sessionTasks.length == 0) {
        note("All your tasks are too big for a game of " + selectedTimeInSecs / 60 + " minutes!");
        break;
      } else {
        console.log('Session full');
        sessionFull = true;
        taskIterator = -1;
        totalSessionTasks = sessionTasks.length;
        console.log(totalSessionTasks);
        globalTimer.set(0, sessionTasksSeconds);
        nextTask();
        globalTimer.run();
        taskTimer.run();
      }
    }
  } else {
    note("You don't have have any tasks in your playlists!");
  }
}
function nextTask() {
  if (taskIterator + 1 < totalSessionTasks) {
    taskIterator++;
    $("#taskText").text(sessionTasks[taskIterator][0]);
    globalTimer.subtract(0, taskTimer.getTotalTimeInSeconds())
    taskTimer.set(sessionTasks[taskIterator][1], sessionTasks[taskIterator][2]);
    $("#taskCounter").text(taskIterator + 1 + " of " + totalSessionTasks + " tasks");
  } else {
    mainMenu();
    taskTimer.stop();
    globalTimer.stop();
    note("Thanks for playing!");
  }
}
//timer object function
function Timer(element, callback) {
  var ac, minutes, seconds, finalTimeInSeconds, displayMinutes, displaySeconds, interval = 1000,
    self = this,
    timeLeftToNextSecond = 1000;
  this.running = false;
  this.set = function(inputMinutes, inputSeconds) {
    finalTimeInSeconds = inputMinutes * 60 + inputSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;
    this.print();
  }
  this.add = function(inputMinutes, inputSeconds) {
    finalTimeInSeconds += inputMinutes * 60 + inputSeconds;
    finalTimeInSeconds = (finalTimeInSeconds < 0) ? 0 : finalTimeInSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;
    this.print();
  }
  this.subtract = function(inputMinutes, inputSeconds) {
    finalTimeInSeconds -= inputMinutes * 60 + inputSeconds;
    if (finalTimeInSeconds <= 0) {
      callback()
    }
    finalTimeInSeconds = (finalTimeInSeconds < 0) ? 0 : finalTimeInSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;
    this.print();
  }
  this.reset = function() {
    this.set(0, 0);
  }
  this.print = function() {
    displayMinutes = (minutes.toString().length == 1) ? "0" + minutes : minutes; //ternary operator: adds a zero to the beggining 
    displaySeconds = (seconds.toString().length == 1) ? "0" + seconds : seconds; //of the number if it has only one caracter.
    $(element).text(displayMinutes + ":" + displaySeconds);
  }
  this.run = function() {
    if (this.running == false) {
      this.running = true;
      var _f = function() {
        secondStarted = new Date;
        self.subtract(0, 1);
        interval = 1000;
      }
      ac = setInterval(_f, interval);
    }
  }
  this.stop = function() {
    if (this.running == true) {
      this.running = false;
      console.log(this + "(" + element + ") was stopped");
      stopped = new Date;
      interval = 1000 - (stopped - secondStarted);
      clearInterval(ac);
    }
  }
  this.getTotalTimeInSeconds = function() {
    return finalTimeInSeconds;
  }
  this.reset();
}
function note(string) {
  alert(string);
}
function mainMenu() {
  //EMPTY BODY
  $("body").empty();
  $("body").append(
    //BUTTONS
    "<div id='playButton' class='mainButton'><div class='buttonText mainButtonText'>PLAY</div></div>"
  );
  //BINDS
  $("#playButton").bind("click", function(){
  	playMain();
    setUpSession();
  });
}
function playMain() {
  //EMPTY BODY
  $("body").empty();
  $("body").append(
    //TASK TEXT
    "<p class='text' id='taskText'>Lorem ipsum dolor sit amet.</p>",
    //TIMERS
    "<div id='taskTimerWrap'><p class='text timer' id='taskTimer'>00:00</p><p class='text' id='taskTimerText'>Task Time</p></div>",
    "<div id='globalTimerWrap'><p class='text timer' id='globalTimer'>00:00</p><p class='text' id='globalTimerText'>Global Time</p></div>",
    //TASK COUNTER
    "<div class='text' id='taskCounter'>0/0 tasks completed</div>"
  );
  //BINDS
  $("#taskText").bind("click", nextTask);
}
#taskText {
  text-align: center;
  display: table;
  vertical-align: middle;
  height: auto;
  width: 100%;
  top: 50px;
  bottom: 0;
  left: 0;
  right: 0;
  position: absolute;
  margin: auto;
  font-size: 65px;
  cursor: pointer;
}
#taskTimerWrap {
  text-align: center;
  top: 0;
  right: 0;
  left: 170px;
  margin: 5px;
  position: absolute;
  -webkit-transition: all 0.5s ease;
}
.timer {
  font-size: 64px;
  margin: 0;
  line-height: 0.88;
}
#taskTimerText {
  font-size: 34.4px;
  margin: 0;
  line-height: 0.65;
}
#globalTimerWrap {
  text-align: center;
  top: 0;
  left: 0;
  right: 170px;
  margin: 5px;
  position: absolute;
}
#globalTimerText {
  font-size: 28.5px;
  margin: 0;
  line-height: 0.78;
  transform: scale(1, 1.2);
}
#taskCounter {
  text-align: center;
  bottom: 0;
  right: 0;
  left: 0;
  width: auto;
  position: absolute;
  font-size: 30px;
  color: #98D8D9;
  -webkit-transition: all 0.5s ease;
}
#taskCounter:hover {
  color: #F1F2F0
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Timer.stop()中,您可以更改用于下一次运行的间隔。更改_f()中的间隔变量不会更改setInterval()使用的间隔。

在这种情况下,您将不得不使用setTimeout()

function Timer(element, callback) {    
  var ac, minutes, seconds, finalTimeInSeconds, displayMinutes, displaySeconds, timeout = 1000,
    self = this,
    timeLeftToNextSecond = 1000;
  this.running = false;
  /* ... */
  this.run = function() {
    if (this.running == false) {
      this.running = true;
      var _f = function() {
        secondStarted = new Date;
        self.subtract(0, 1);        
        ac = setTimeout(_f, 1000);
      }
      ac = setTimeout(_f, timeout);
    }
  }
  this.stop = function() {    
    if (this.running == true) {
      this.running = false;
      console.log(this + "(" + element + ") was stopped");
      stopped = new Date;
      timeout = 1000 - (stopped - secondStarted);
      clearTimeout(ac);
    }
  }
  /* ... */    
}

正如我所看到的,您正试图从interval回调函数内部设置变量interval的值。

      var _f = function() {
        secondStarted = new Date;
        self.subtract(0, 1);
        //interval = 1000; REMOVE THIS LINE
      }
      interval = 1000; //ADD IT HERE
      ac = setInterval(_f, interval);

实际上,当执行setInterval时,interval的值不是1000。它还没有被_f更新,因为该函数将在执行setInterval()之后运行。一旦用interval的现有值调用setInterval(),以后更改它对创建的间隔没有影响,因为setInterval()函数已经为创建的间隔设置了延迟时间。interval的值从其初始值1000发生变化,因为这一行:

 interval = 1000 - (stopped - secondStarted); //I'm not sure what you are trying to do with this, possibly removing this line will also fix your problem.)

完整的工作演示:

这是JS Fiddle。

mainMenu();
var totalSessionTasks, taskIterator, selectedTimeInSecs = 300;
var taskTimer = new Timer("#taskTimer", nextTask);
var globalTimer = new Timer("#globalTimer", function() {
});
var tasks = [
  ["First task", 0, 30],
  ["Second task", 0, 15],
  ["Third task", 0, 10],
  ["Fourth task", 3, 0]
];
var sessionTasks = [
]

function setUpSession() {
  sessionTasks = []
  if (tasks.length != 0) {
    var sessionTasksSeconds = 0; //the seconds of the session being filled
    var sessionTasksSecondsToFill = selectedTimeInSecs; //seconds left in the session to fill
    var newTaskSeconds = 0; //seconds of the next task being added to the session
    var sessionFull = false;
    console.log('Session Empty');
    while (sessionFull === false) {
      var areThereAnyTaskThatFitInTheSession =
        tasks.some(function(item) {
          return ((item[1] * 60 + item[2]) <= sessionTasksSecondsToFill) && (item != sessionTasks[sessionTasks.length - 1]);
        });
      console.log(areThereAnyTaskThatFitInTheSession);
      if (areThereAnyTaskThatFitInTheSession) {
        do {
          var randTaskNum = Math.floor(Math.random() * tasks.length);
        } while (((tasks[randTaskNum][1] * 60 + tasks[randTaskNum][2]) > sessionTasksSecondsToFill) || (tasks[randTaskNum] == sessionTasks[sessionTasks.length - 1]))
        sessionTasks.push(tasks[randTaskNum]);
        newTaskSeconds = (tasks[randTaskNum][1]) * 60 + tasks[randTaskNum][2];
        sessionTasksSecondsToFill -= newTaskSeconds;
        sessionTasksSeconds += newTaskSeconds;
        console.log(tasks[randTaskNum][0] + ": " + newTaskSeconds + "s");
        console.log(sessionTasksSeconds)
      } else if (sessionTasks.length == 0) {
        note("All your tasks are too big for a game of " + selectedTimeInSecs / 60 + " minutes!");
        break;
      } else {
        console.log('Session full');
        sessionFull = true;
        taskIterator = -1;
        totalSessionTasks = sessionTasks.length;
        console.log(totalSessionTasks);
        globalTimer.set(0, sessionTasksSeconds);
        nextTask();
        globalTimer.run();
        taskTimer.run();
      }
    }
  } else {
    note("You don't have have any tasks in your playlists!");
  }
}
function nextTask() {
  if (taskIterator + 1 < totalSessionTasks) {
    taskIterator++;
    $("#taskText").text(sessionTasks[taskIterator][0]);
    globalTimer.subtract(0, taskTimer.getTotalTimeInSeconds())
    taskTimer.set(sessionTasks[taskIterator][1], sessionTasks[taskIterator][2]);
    $("#taskCounter").text(taskIterator + 1 + " of " + totalSessionTasks + " tasks");
  } else {
    mainMenu();
    taskTimer.stop();
    globalTimer.stop();
    note("Thanks for playing!");
  }
}
//timer object function
function Timer(element, callback) {
  var ac, minutes, seconds, finalTimeInSeconds, displayMinutes, displaySeconds, interval = 1000,
    self = this,
    timeLeftToNextSecond = 1000;
  this.running = false;
  this.set = function(inputMinutes, inputSeconds) {
    finalTimeInSeconds = inputMinutes * 60 + inputSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;
    this.print();
  }
  this.add = function(inputMinutes, inputSeconds) {
    finalTimeInSeconds += inputMinutes * 60 + inputSeconds;
    finalTimeInSeconds = (finalTimeInSeconds < 0) ? 0 : finalTimeInSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;
    this.print();
  }
  this.subtract = function(inputMinutes, inputSeconds) {
    finalTimeInSeconds -= inputMinutes * 60 + inputSeconds;
    if (finalTimeInSeconds <= 0) {
      callback()
    }
    finalTimeInSeconds = (finalTimeInSeconds < 0) ? 0 : finalTimeInSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;
    this.print();
  }
  this.reset = function() {
    this.set(0, 0);
  }
  this.print = function() {
    displayMinutes = (minutes.toString().length == 1) ? "0" + minutes : minutes; //ternary operator: adds a zero to the beggining 
    displaySeconds = (seconds.toString().length == 1) ? "0" + seconds : seconds; //of the number if it has only one caracter.
    $(element).text(displayMinutes + ":" + displaySeconds);
  }
  this.run = function() {
    if (this.running == false) {
      this.running = true;
      var _f = function() {
        secondStarted = new Date;
        self.subtract(0, 1);
        //interval = 1000; REMOVE THIS LINE
      }
      interval = 1000; //ADD IT HERE
      ac = setInterval(_f, interval);
    }
  }
  this.stop = function() {
    if (this.running == true) {
      this.running = false;
      console.log(this + "(" + element + ") was stopped");
      stopped = new Date;
      interval = 1000 - (stopped - secondStarted);
      clearInterval(ac);
    }
  }
  this.getTotalTimeInSeconds = function() {
    return finalTimeInSeconds;
  }
  this.reset();
}
function note(string) {
  alert(string);
}
function mainMenu() {
  //EMPTY BODY
  $("body").empty();
  $("body").append(
    //BUTTONS
    "<div id='playButton' class='mainButton'><div class='buttonText mainButtonText'>PLAY</div></div>"
  );
  //BINDS
  $("#playButton").bind("click", function(){
  	playMain();
    setUpSession();
  });
}
function playMain() {
  //EMPTY BODY
  $("body").empty();
  $("body").append(
    //TASK TEXT
    "<p class='text' id='taskText'>Lorem ipsum dolor sit amet.</p>",
    //TIMERS
    "<div id='taskTimerWrap'><p class='text timer' id='taskTimer'>00:00</p><p class='text' id='taskTimerText'>Task Time</p></div>",
    "<div id='globalTimerWrap'><p class='text timer' id='globalTimer'>00:00</p><p class='text' id='globalTimerText'>Global Time</p></div>",
    //TASK COUNTER
    "<div class='text' id='taskCounter'>0/0 tasks completed</div>"
  );
  //BINDS
  $("#taskText").bind("click", nextTask);
}
#taskText {
  text-align: center;
  display: table;
  vertical-align: middle;
  height: auto;
  width: 100%;
  top: 50px;
  bottom: 0;
  left: 0;
  right: 0;
  position: absolute;
  margin: auto;
  font-size: 65px;
  cursor: pointer;
}
#taskTimerWrap {
  text-align: center;
  top: 0;
  right: 0;
  left: 170px;
  margin: 5px;
  position: absolute;
  -webkit-transition: all 0.5s ease;
}
.timer {
  font-size: 64px;
  margin: 0;
  line-height: 0.88;
}
#taskTimerText {
  font-size: 34.4px;
  margin: 0;
  line-height: 0.65;
}
#globalTimerWrap {
  text-align: center;
  top: 0;
  left: 0;
  right: 170px;
  margin: 5px;
  position: absolute;
}
#globalTimerText {
  font-size: 28.5px;
  margin: 0;
  line-height: 0.78;
  transform: scale(1, 1.2);
}
#taskCounter {
  text-align: center;
  bottom: 0;
  right: 0;
  left: 0;
  width: auto;
  position: absolute;
  font-size: 30px;
  color: #98D8D9;
  -webkit-transition: all 0.5s ease;
}
#taskCounter:hover {
  color: #F1F2F0
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

以下是jsfiddle 的更新

问题出在Timer 的变量interval

这条线在Timer.stop():下产生了不可预测的间隔

interval = 1000 - (stopped - secondStarted);

如果您想缩短时间间隔,可以添加play_count属性:

    ...
    //timer object function
    function Timer(element, callback) {
  var ac, minutes, seconds, finalTimeInSeconds, displayMinutes, displaySeconds, interval = 1000,
    self = this,
    timeLeftToNextSecond = 1000;
  this.running = false;
 play_count = 0;  // play count property
...
  this.run = function() {
    if (this.running == false) {
      this.running = true;
      var _f = function() {
        secondStarted = new Date;
        self.subtract(0, 1);
        interval = Math.max(1000 - play_count * 100, 500);  // ** <-- shorten time interval
      }
      ac = setInterval(_f, interval);

    }
  }
  this.stop = function() {
    if (this.running == true) {
      this.running = false;
      console.log(this + "(" + element + ") was stopped");
//      stopped = new Date;
//      interval = 1000 - (stopped - secondStarted);
            play_count++;
      clearInterval(ac);
    }
  }
  this.getTotalTimeInSeconds = function() {

    return finalTimeInSeconds;
  }
  this.reset();
}
...

最新更新