Анимация JQuery - есть ли способ «остановить» полный триггер? - PullRequest
5 голосов
/ 07 августа 2011

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

    ...
    .css({
        'left': BASE_NODE_INIT_LEFT_PX,
        'top' : rand(BASE_NODE_INIT_TOP_MIN_PX, BASE_NODE_INIT_TOP_MAX_PX) + 'px',
        'width': _width + 'px',
        'height': _height + 'px',
    })
    .animate({
        left: BASE_NODE_ANIMATE_DISTANCE_X_PX
    }
    ...

Достаточно простая анимация.Вот забавная часть: на каждом шаге я хотел бы применить некоторую физику к этому элементу.

Примерно так:

        ...
        step:function(currentStep){
            if($(this).data('animating-wind') !== true)
            {
                $(this).data('animating-wind',true);
                var wind = (rand(1,2) == 2) ? '+=' + rand(1, 100) + 'px' : '-=' + rand(1, 100) + 'px';
                $(this).animate({
                    'top': wind,
                    'text-indent': wind,
                },{
                    duration: 500,
                    complete: function(){
                        $(this).data('animating-wind',false);
                    },
                    queue: false
                });
            }
        }
        ...

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

Проблема, с которой я сталкиваюсь, заключается в том, что иногда «ветер» достаточно сильный, чтобы элемент все еще был виден в окне просмотра, когда количество шагов достигло общего рассчитанного шага, и он просто исчезнет (что происходит в моем complete:function(){ ...$(this).remove(); ...}

Очевидно, что происходит то, что JQuery думает, что анимация завершена, потому что он рассчитал шаги и сказал: «Я анимирую этот объект так много пикселей за эти много миллисекунд за эти много шагов -это там ". Но анимация ветра находится вне очереди анимации , поэтому этот процесс анимации не имеет смысла.

Я хотел бы начать анимацию и просто продолжать анимацию доэлемент покинул область просмотра в любом направлении - как только он больше не будет виден ни с начальной траектории полета, ни из-за ветра, уносящего его с экрана, я обнаружу его и вызову обратный вызов onComplete. Единственное, что я могудумать о том, чтобы поместить эту анимацию внутри функции и в complete:function(){} сделать рекурсивныйЯ позвонил сам себе, затем внутри step:function(){} я бы сделал проверку «видно в окне просмотра» и позвонил бы stop(true,false), если true.Это просто кажется очень дорогой проверкой - выполнение этого вычисления более 100 раз за 400-1200 мс может сделать анимацию прерывистой, поэтому я ищу более элегантное решение.

TL; DR: Как мне продолжать анимировать что-то, пока оно еще не достигло своего пункта назначения из-за установки на него дополнительных анимаций?

ОБНОВЛЕНИЕ: Я применил @ mu слишкомИдея Шорта и пришла с этим:

/** 
 * Create a new clone of the master div for user interaction. Position it 
 * randomly off the canvas, and animate it across at a random speed.
 */

function spawn_target() {
    spawn_timeout = setTimeout(function() {
        var bonus = (rand(1, BONUS_ODDS) == BONUS_ODDS) ? ' ' + BONUS_CLASS : '';
        var _img_src = (bonus.length > 0) ? BONUS_NODE_IMG_SRC : BASE_NODE_IMG_SRC;
        var _width = $('#' + BASE_NODE_ID).width();
            _width = Math.floor(rand(_width / 3, _width));
        var _height = _width;
        var _framerate = 10 - Math.floor(_height/10);
        var _clone = $('#' + BASE_NODE_ID).clone().addClass(CLONE_CLASS + bonus).attr('id', CLONE_ID).appendTo('#' + CANVAS_ID).css({
            'left': BASE_NODE_INIT_LEFT_PX,
            'top': rand(BASE_NODE_INIT_TOP_MIN_PX, BASE_NODE_INIT_TOP_MAX_PX) + 'px',
            'width': _width + 'px',
            'height': _height + 'px',
        })

        $(_clone).children('img').attr('src', _img_src);

        /**
         * Handle the actual flight across the viewport 
         * @param object _this The clone we are animating manually
         * @return object pointer This contains the interval so we can clear it later
        */
        function fly(_this) {

            var animating_wind = false;
            var pointer = {
                timer_id: null
            };
            pointer.timer_id = setInterval(function() {

                // Get rid of the node if it is no longer visible in the viewport
                if (!$(_this).is(':onviewport')) {
                    clearInterval(pointer.timer_id);
                    $(_this).remove();
                    adj_game_data(GAME_DATA_LIVES_ID, -1);
                    check_lives();
                    spawn_target();
                }

                // Move node along with slight realism
                var distance = rand(FLY_DISTANCE_MIN, FLY_DISTANCE_MAX);
                $(_this).css('left', '-=' + distance + 'px');

                // Only trigger the wind animation when it is idle
                if (animating_wind !== true) {
                    animating_wind = true;

                    // Setup the wind physics
                    var _wind_power = rand(WIND_POWER_MIN, WIND_POWER_MAX);
                    var _wind_dir = (rand(1, 2) == 2) ? '+=' : '-=';

                    // Override wind direction to keep node inside viewport
                    if(($(_this).offset().top + $(_this).height() + _wind_power) > CANVAS_HEIGHT)
                    {
                        _wind_dir = '-=';
                    }
                    if(($(_this).offset().top - _wind_power) < CANVAS_TOP_BUFFER_PX)
                    {
                         _wind_dir = '+=';   
                    }

                    var _wind = _wind_dir + _wind_power + 'px';  

                    // Apply the wind physics and don't let the node break the top or bottom
                    // boundaries of the viewport
                    $(_this).animate({
                        'top': _wind
                    }, {
                        duration: WIND_ANIMATION_DURATION,
                        complete: function() {
                            animating_wind = false;
                        },
                        queue: false
                    });
                }
            }, _framerate);

            return pointer;
        }
        $(_clone).data('interval', fly(_clone));

    }, rand(SPAWN_TARGET_TIMEOUT_MIN, SPAWN_TARGET_TIMEOUT_MAX));
}

Вот что у меня есть: http://jsfiddle.net/AlienWebguy/DmtQ7/embedded/result/

Мне нравится эта идея, хотя, похоже, в JQuery должно быть что-то встроенное.на что я и надеялся.Теперь вместо $(obj).stop() мне нужно clearInterval($(obj).data('interval').timer_id);.* царапает голову ...

1 Ответ

1 голос
/ 07 августа 2011

Это то, что, я думаю, вам следует делать (псевдо-JavaScript):

function animate_it($thing) {
    var wind     = false;
    var timer_id = setInterval(function() {
        // Compute the movement for this step.
        // Then factor in the wind effect and update the `wind` variable.
        // Then apply the movement to `$thing`.
        if(it_is_off_the_page($thing))
            clearInterval(timer_id);
    }, n);
}

Если во внутреннем вычислении есть что-то, что вычисляется снова и снова, предварительно вычислите его в animate_itуровень и пусть закрытие над ним.Вам бы хотелось немного поиграть с ним, чтобы выяснить, каким должно быть n.

Просто выполните анимацию вручную, чтобы не бороться с функцией animate в jQuery и остановить анимацию, когда вызнаю, что сделано.Это также позволяет вам указать направление и силу ветра (чтобы ваш анимированный объект мог двигаться назад) практически бесплатно.

Возможно, вы захотите вернуть timer_id обратно во внешний мир, чтобыВы можете остановить все это снаружи.В этом случае вы захотите подделать указатель с объектом из одного элемента:

function animate_it($thing) {
    var wind    = false;
    var pointer = { timer_id: null };
    pointer.timer_id = setInterval(function() {
        // Compute the movement for this step.
        // Then factor in the wind effect and update the `wind` variable.
        // Then apply the movement to `$thing`.
        if(it_is_off_the_page($thing)) {
            clearInterval(pointer.timer_id);
            pointer.timer_id = null;
        }
    }, n);
    return pointer;
}

var timer = animate_it($thing);

// And then later, to shut it down...
if(timer.timer_id)
    clearInterval(timer.timer_id);

Вы, вероятно, захотите обратный вызов «все прошло» на animate_it, если у вас много анимированныхобъекты, которые позволят вам сохранить ваш внешний список таймеров чистыми и бесполезными.


Проблема с попыткой сделать это с помощью animate состоит в том, что animate вычисляет количество шагов в началеи тогда вы можете изменить это.Вам нужно переменное количество шагов, но animate это не волнует (это заботится даже меньше, чем барсук с медом).

...