Периодическая анимация числа с помощью Javascript

У меня есть элемент на моей странице, который я хотел бы время от времени увеличивать:

<p class="count">28</p>

В моем javascript у меня есть интервал, который запускается каждые 5 секунд и асинхронно получает новый (более высокий) номер с сервера. Это работает примерно так же, как счетчик.

setInterval(function(){
  $.post("getcount.php", function(num){
    latestNum = num;
  });
}, 5000);

Если асинхронный запрос выводит число 56, я бы хотел, чтобы текст абзаца изменился с 28 на 56, показывая многие (или большинство) или промежуточные звенья по мере его увеличения. Сейчас я устанавливаю другой интервал для постоянной проверки текста абзаца и локальной переменной. Если текст абзаца меньше значения переменной, он увеличивает его на 1. Он делает это каждые полсекунды.

setInterval(function(){
  currNum = Number( $(".count").text() );
  if (currNum < latestNum)
    $(".count").text( currNum + 1 );
}, 50);

Мой вопрос заключается в следующем: есть ли лучший способ сделать это без постоянного запуска интервала? Можно ли вызвать это анимированное приращение из ответа на асинхронный запрос, а затем остановить его, как только два числа сойдутся в значении? Я также должен отметить, что существует вероятность того, что следующий запрос будет выполнен до того, как два числа будут согласованы по значению.

Как коллектив вы сделал бы это?


person Sampson    schedule 25.02.2010    source источник


Ответы (4)


Вот мой ответ - на самом деле два ответа.
Линейное продвижение к целевому значению с фиксированным интервалом рискует никогда его не достичь.
Поэтому я также предоставил другое решение, которое каждый раз уменьшает разницу вдвое, и достигает его намного быстрее даже для большие различия.
Оба примера также могут вести обратный отсчет.

$( function() {

    var tRef,
        counter = $( '#counter' );
        target  = $( '#target' );

    /*
     * A function that eases towards its target
     */
    function convergeEasing( target ) {
        clearTimeout( tRef );
        target = parseInt( target );
        var current = parseInt( counter.html() );
        var diff = target - current;
        if( diff ) {
            var rounder = diff > 0 ? Math.ceil : Math.floor;
            current += rounder( diff/2 );
            counter.html( current );
            tRef = setTimeout( function() {
                convergeEasing( target );
            }, 250 );
        }
    }

    /*
     * A function that plods towards its target
     */
    function convergeLinear( target ) {
        clearTimeout( tRef );
        target = parseInt( target );
        var current = parseInt( counter.html() );
        var diff = target - current;
        if( diff ) {
            current += diff > 0 ? 1 : -1;
            counter.html( current );
            tRef = setTimeout( function() {
                convergeLinear( target );
            }, 250 );
        }
    }

    /*
     * I've mocked your ajax request up here for demo purposes
     * using the linear way as per your question
     */
    setInterval( function(){
        var n = Math.round( Math.random()*1000 );
        target.html( n );
        convergeLinear( n );
    }, 5000 );

});

<div id="target">20</div>
<div id="counter">20</div>

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

person meouw    schedule 25.02.2010

Исправленный ответ:

Комментаторам: Сам понял... все равно спасибо!

var interval = setInterval(function(){
  currNum = Number( $(".count").text() );
  if (currNum < latestNum) {
    $(".count").text( currNum + 1 );
  } else {
    setTimeOut(function() { clearInterval(interval) }, 0);
  }
}, 50);

Это сохраняет интервал в переменной, а затем вызывает clearInterval, чтобы остановить интервал. setTimeOut необходим, так как в противном случае интервал будет очищен при его выполнении.

Первый ответ:

Почему бы не объединить две функции, т.е.:

setInterval(function(){
  $.post("getcount.php", function(num){
    latestNum = num;
    if (currNum < latestNum) {
        currNum = latestNum;
        $(".count").text( currNum );
    }
  });
}, 5000);

Это предотвратило бы обновление каждые полсекунды.

person AxelEckenberger    schedule 25.02.2010
comment
Я хочу анимировать через промежуточные звенья. Этот подход просто заменит текущее значение новым значением. - person Sampson; 25.02.2010

Основываясь на ответе Obalix, я бы рекомендовал иметь 5-секундный интервал запускает меньший интервал.

var interval = null, currNum = null;
setInterval(function(){
  $.post("getcount.php", function(num){
    latestNum = num;
    if (currNum === null)
        currNum = Number( $(".count").text() ); // should only execute once
    if (currNum < latestNum && interval === null)
        interval = setInterval(ObalixFunction, 50);
  });
}, 5000);

и изменить

function ObalixFunction() {
    if (currNum < latestNum) {
        // increment the currNum variable too so we don't have to keep parsing it
        $(".count").text( ++currNum ); 
    } else {
        setTimeOut(function() { clearInterval(interval); interval = null; }, 0);
    }
}
person Joel    schedule 25.02.2010

Я бы создал второй интервал, когда был получен результат, и завершил его, когда было достигнуто целевое значение.

person Lazarus    schedule 25.02.2010