Как использовать setInterval () и clearInterval () в одной функции - PullRequest
1 голос
/ 12 февраля 2020

Я пытаюсь создать функцию, которая запускает и останавливает таймер. Запуск всегда осуществляется нажатием кнопки, но остановка может быть вызвана отключением таймера или повторным вызовом функции из другой функции.

Это то, что я имею до сих пор. Прекрасно работает для того, что вы видите, но я не могу понять, как включить clearInterval (), чтобы он останавливался, когда игра выиграна. Действующий вызывающий timerCountdown находится в другом файле js. Я читал ответы на похожие вопросы, но все они, кажется, делают это немного иначе, чем там, где я не могу заставить это работать для моего случая

Я понимаю, что мне нужно вызвать clearInterval (count) но я не знаю, как включить это в саму функцию.

const timerCountdown = () => {                             
  let count = setInterval(() => {
    let timeLeft = timer.innerHTML
    if (timeLeft > 0) {
      timer.innerHTML = timeLeft - 1
    } else {
      gameOverMessage()
    }
  }, 1000) 
}

Ответы [ 3 ]

2 голосов
/ 12 февраля 2020

Вам нужно указать pu sh идентификатор интервала в глобальной переменной. Например, вы можете использовать другую функцию для остановки интервала, когда хотите.

Как и

let intervalId; // define `count` globaly
let timer = document.getElementById('timer')

const timerStop = () => {
  clearInterval(intervalId)
}

const timerRestart = () => {
  timerStop()
  timer.innerHTML = 100;
}

const timerStart = () => {
  timerStop(); // Stop timer by security, e.g. if you call `timerStart()` multiple times
  intervalId = setInterval(() => {
    let timeLeft = timer.innerHTML;
    if (+timeLeft > 0) {
      timer.innerHTML = +timeLeft - 1
    } else {
      timerRestart();
      gameOverMessage()
    }
  }, 1000)
}
<div id="timer">100</div>

<div>
  <button onclick="timerStart()">Start</button>
  <button onclick="timerStop()">Pause</button>
  <button onclick="timerRestart()">Restart</button>
</div>
2 голосов
/ 12 февраля 2020

setInterval делает все возможное, чтобы пространство выполнения обратного вызова соответствовало заданному интервалу. Дело в том, что в игре вам действительно нужно, чтобы текущее состояние мира отображалось на экране плавно и своевременно. Это отличается от поведения setInterval, который ничего не знает об экране и неоднократно вызывается вслепую.

Например: если вы запускаете setInterval(foo, 100) для своей игры на вкладке браузера, а затем перейдите на другую вкладку в вашем браузере, подождите десять секунд, а затем вернитесь в свою игру, ваш foo обратный вызов будет вызываться около ста раз подряд , поскольку поставленные в очередь обратные вызовы "истощаются". Маловероятно, что вам нужно такое поведение.

requestAnimationFrame является лучшим решением для этого, потому что оно вызывается только тогда, когда (незадолго до этого) ваша игра рендерится - то, что вы want.

В следующем коде объект таймера создается createTimer. Таймер имеет методы start, stop и toggle.

Метод start записывает, когда он был вызван, и запускает requestAnimationFrame, предоставляя обратный вызов с именем tick. Каждый раз, когда происходит тик, мы запускаем некоторый logi c, чтобы увидеть, какой (если таковой имеется) обратный вызов вызывать.

Если истекшее время больше или равно duration таймера, то onTimeout обратный вызов вызывается и таймер останавливается.

Если прошедшее время меньше, чем duration, но больше или равно периоду interval, то мы обновляем lastInterval и вызовите обратный вызов onInterval.

В противном случае мы просто включим еще один тик таймера.

Метод stop просто использует идентификатор анимации запроса для отмены таймера с помощью cancelAnimationFrame.

function createTimer() {
    let rafId = null

    function start({duration = 10000, interval = 1000, onInterval, onTimeout, onStop, startTime=performance.now(), lastInterval = startTime}) {
        function tick(now=performance.now()) {
            const elapsed = now - startTime 
            if (elapsed >= duration) {
                cancelAnimationFrame(rafId)
                rafId = null
                return onTimeout()
            }

            if ((now - lastInterval) >= interval) {
                lastInterval = now
                onInterval({
                    duration,
                    elapsed
                })
            }

            rafId = requestAnimationFrame(tick)
        }
        rafId = requestAnimationFrame(tick)
    }

    function stop() {
        cancelAnimationFrame(rafId)
        rafId = null
        return onStop()
    }

    function toggle(...args) {
        rafId ? stop() : start(...args)
    }

    const timer = {
        start,
        stop,
        toggle
    }

    return timer
}

const timer = createTimer()

const onInterval = ({duration, elapsed})=>console.log(`Remaining: ${((duration - elapsed)/1000).toFixed(0)}`)
const onTimeout = ()=>console.log('Timed out.')
const onStop = ()=>console.log('Manually stopped.')

document.getElementById('btn').addEventListener('click', () => timer.toggle({
    onInterval,
    onTimeout,
    onStop
}))
<button id="btn">Toggle Timer</button>
1 голос
/ 12 февраля 2020

Вы можете взять глобальную переменную intervalId и очистить интервальный таймер, если выиграете или нет времени.

var intervalId;

const timerCountdown = () => {                             
    intervalId = setInterval(() => {
        let timeLeft = timer.innerHTML
        if (timeLeft > 0) {
            timer.innerHTML = timeLeft - 1
        } else {
            clearInterval(intervalId);
            gameOverMessage();
        }
    }, 1000) 
  },
  won = () => {
      clearInterval(intervalId);
      // additional code
  };
...