web-dev-qa-db-ja.com

Javascript-setInterval()の一時停止

Javascriptを使用してsetInterval()関数を一時停止および再開するにはどうすればよいですか?

たとえば、ウェブページを見ている秒数を知らせるストップウォッチがあります。 [一時停止]ボタンと[再開]ボタンがあります。 clearInterval()がここでは機能しないの理由は、ユーザーが40秒と800ミリ秒で「一時停止」ボタンをクリックした場合、「再開」ボタンをクリックすると、経過した秒数は、200ミリ秒後に1増加する必要があります。タイマー変数でclearInterval()関数を使用して(一時停止ボタンがクリックされたとき)、タイマー変数で再びsetInterval()関数を使用すると(再開ボタンがクリックされたとき)、経過した秒数が増加します1000ミリ秒後にのみ1。ストップウォッチの精度が損なわれます。

それでどうすればいいですか?

44
chris97ong

フラグを使用してステータスを追跡できます。

var output = $('h1');
var isPaused = false;
var time = 0;
var t = window.setInterval(function() {
  if(!isPaused) {
    time++;
    output.text("Seconds: " + time);
  }
}, 1000);

//with jquery
$('.pause').on('click', function(e) {
  e.preventDefault();
  isPaused = true;
});

$('.play').on('click', function(e) {
  e.preventDefault();
  isPaused = false;
});
h1 {
    font-family: Helvetica, Verdana, sans-serif;
    font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Seconds: 0</h1>
<button class="play">Play</button>
<button class="pause">Pause</button>

これは私がやろうとしていることであり、実際にsetIntervalを一時停止できるかどうかはわかりません。

注:このシステムは簡単で、高レベルの精度を必要としないアプリケーションでは非常によく機能しますが、ティック間の経過時間は考慮しません。 0.5秒オフになります。

92
Jonas Grumann

インターバル機能で時間を測定しないでください。代わりに、タイマーが開始されたときだけ時間を節約し、タイマーが停止/一時停止したときの差を測定します。表示された値を更新するためにのみsetIntervalを使用します。したがって、タイマーを一時停止する必要はなく、この方法で最高の精度を得ることができます。

9
VitaliyG

@Jonas Giuroがそう言っているのは正しいことですが、

SetInterval関数を一時停止することはできません。停止する(clearInterval)か、実行させることができます

一方、この動作は approach @VitaliyG推奨 でシミュレートできます。

インターバル機能で時間を測定しないでください。代わりに、タイマーが開始されたときだけ時間を節約し、タイマーが停止/一時停止したときの差を測定します。表示された値を更新するためにのみsetIntervalを使用します。

var output = $('h1');
var isPaused = false;
var time = new Date();
var offset = 0;
var t = window.setInterval(function() {
  if(!isPaused) {
    var milisec = offset + (new Date()).getTime() - time.getTime();
    output.text(parseInt(milisec / 1000) + "s " + (milisec % 1000));
  }
}, 10);

//with jquery
$('.toggle').on('click', function(e) {
  e.preventDefault();
  isPaused = !isPaused;
  if (isPaused) {
    offset += (new Date()).getTime() - time.getTime();
  } else {
    time = new Date();
  }

});
h1 {
    font-family: Helvetica, Verdana, sans-serif;
    font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Seconds: 0</h1>
<button class="toggle">Toggle</button>
7
sbgoran

より単純なアプローチを使用しないのはなぜですか?クラスを追加してください!

間隔に何もしないように指示するクラスを追加するだけです。例:ホバー時。

var i = 0;
this.setInterval(function() {
  if(!$('#counter').hasClass('pauseInterval')) { //only run if it hasn't got this class 'pauseInterval'
    console.log('Counting...');
    $('#counter').html(i++); //just for explaining and showing
  } else {
    console.log('Stopped counting');
  }
}, 500);

/* In this example, I'm adding a class on mouseover and remove it again on mouseleave. You can of course do pretty much whatever you like */
$('#counter').hover(function() { //mouse enter
    $(this).addClass('pauseInterval');
  },function() { //mouse leave
    $(this).removeClass('pauseInterval');
  }
);

/* Other example */
$('#pauseInterval').click(function() {
  $('#counter').toggleClass('pauseInterval');
});
body {
  background-color: #eee;
  font-family: Calibri, Arial, sans-serif;
}
#counter {
  width: 50%;
  background: #ddd;
  border: 2px solid #009afd;
  border-radius: 5px;
  padding: 5px;
  text-align: center;
  transition: .3s;
  margin: 0 auto;
}
#counter.pauseInterval {
  border-color: red;  
}
<!-- you'll need jQuery for this. If you really want a Vanilla version, ask -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


<p id="counter">&nbsp;</p>
<button id="pauseInterval">Pause</button></p>

私はこの高速で簡単なアプローチを長い間探していたので、できるだけ多くの人を紹介するためにいくつかのバージョンを投稿しています。

5
Aart den Braber

私はこのスレッドが古いことを知っていますが、これは別の解決策かもしれません:

var do_this = null;

function y(){
   // what you wanna do
}

do_this = setInterval(y, 1000);

function y_start(){
    do_this = setInterval(y, 1000);
};
function y_stop(){
    do_this = clearInterval(do_this);
};
1
M1ke

次のコードは、タイマーを再開する一時停止の正確な方法を提供します。

仕組み:

タイマーが一時停止後に再開されると、補正サイクルを生成します単一のtimeoutを使用すると、一時停止オフセット(サイクル間でタイマーが一時停止された正確な時間)が考慮されます。修正サイクルが終了すると、通常のsetIntevalで次のサイクルをスケジュールし、通常のサイクル実行を継続します。

これにより、同期を失うことなく、タイマーを一時停止/再開できます。

コード:

function Timer(_fn_callback_ , _timer_freq_){
    let RESUME_CORRECTION_RATE = 2;

    let _timer_statusCode_;
    let _timer_clockRef_;

    let _time_ellapsed_;        // will store the total time ellapsed
    let _time_pause_;           // stores the time when timer is paused
    let _time_lastCycle_;       // stores the time of the last cycle

    let _isCorrectionCycle_;
 
    /**
     * execute in each clock cycle
     */
    const nextCycle = function(){
        // calculate deltaTime
        let _time_delta_        = new Date() - _time_lastCycle_;
        _time_lastCycle_    = new Date();
        _time_ellapsed_   += _time_delta_;

        // if its a correction cicle (caused by a pause,
        // destroy the temporary timeout and generate a definitive interval
        if( _isCorrectionCycle_ ){
            clearTimeout( _timer_clockRef_ );
            clearInterval( _timer_clockRef_ );
            _timer_clockRef_    = setInterval(  nextCycle , _timer_freq_  );
            _isCorrectionCycle_ = false;
        }
        // execute callback
        _fn_callback_.apply( timer, [ timer ] );
    };

    // initialize timer
    _time_ellapsed_     = 0;
    _time_lastCycle_     = new Date();
    _timer_statusCode_   = 1;
    _timer_clockRef_     = setInterval(  nextCycle , _timer_freq_  );


    // timer public API
    const timer = {
        get statusCode(){ return _timer_statusCode_ },
        get timestamp(){
            let abstime;
            if( _timer_statusCode_=== 1 ) abstime = _time_ellapsed_ + ( new Date() - _time_lastCycle_ );
            else if( _timer_statusCode_=== 2 ) abstime = _time_ellapsed_ + ( _time_pause_ - _time_lastCycle_ );
            return abstime || 0;
        },

        pause : function(){
            if( _timer_statusCode_ !== 1 ) return this;
            // stop timers
            clearTimeout( _timer_clockRef_ );
            clearInterval( _timer_clockRef_ );
            // set new status and store current time, it will be used on
            // resume to calculate how much time is left for next cycle
            // to be triggered
            _timer_statusCode_ = 2;
            _time_pause_       = new Date();
            return this;
        },

        resume: function(){
            if( _timer_statusCode_ !== 2 ) return this;
            _timer_statusCode_  = 1;
            _isCorrectionCycle_ = true;
            const delayEllapsedTime = _time_pause_ - _time_lastCycle_;
            _time_lastCycle_    = new Date( new Date() - (_time_pause_ - _time_lastCycle_) );

            _timer_clockRef_ = setTimeout(  nextCycle , _timer_freq_ - delayEllapsedTime - RESUME_CORRECTION_RATE);

            return this;
        } 
    };
    return timer;
};


let myTimer = Timer( x=> console.log(x.timestamp), 1000);
<input type="button" onclick="myTimer.pause()" value="pause">
<input type="button" onclick="myTimer.resume()" value="resume">

コードソース:

このタイマーは、advanced-timerの修正および簡略化されたバージョンであり、自分で作成したjsライブラリで、さらに多くの機能を備えています。

完全なライブラリとドキュメントは [〜#〜] npm [〜#〜] および [〜#〜] github [〜#〜] で入手できます。

0
colxi