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

时间:2015-12-27 20:12:50

标签: javascript jquery timer

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

我正在开发一个个人培训网络应用程序,基本上你可以玩游戏,然后你可以按照随机顺序完成一系列任务。该程序创建sessionTasks数组,其中放入tasks数组的随机顺序任务,以符合五分钟限制。现在,tasks数组只是我用四个任务创建的数组,以及用于测试的相应时间。

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

  1. 点击播放。
  2. 通过快速单击任务文本来完成任务。
  3. 再次点击播放。
  4. 现在秒应该更快地移动。如果没有,重复你刚刚做的。这是不规则的,但通常在第二次尝试时就会这样做。

    我不能为我的生活理解为什么它会像这样。我想也许是创造了更多使用#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);
    }
    &#13;
    #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
    }
    &#13;
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    &#13;
    &#13;
    &#13;

3 个答案:

答案 0 :(得分:2)

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);
    }
  }

  /* ... */    
}

答案 1 :(得分:1)

以下是jsfiddle

中的更新

问题出在interval

的变量Timer

此行在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();

}
...

答案 2 :(得分:1)

正如我所看到的,您正在尝试从间隔回调函数中设置变量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()执行后运行。一旦使用现有值setInterval()调用interval,稍后更改它对创建的间隔没有影响,因为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>