Останавливая на jQuery.затем цепочка через пользовательский ввод


Это, вероятно, простой вопрос, но я совершенно потерялся.

У меня есть эта функция.

m.util.genericSwipeVertFunc = function (
        ajaxRequest,
        swipeOutTarget,
        swipeInTarget
) {
    var stage1, stage2, failStage, dfd = $.Deferred(), finalStage, functionPromise;

    // Swipe of screen wait for ajax request
    stage1 = function () {
        return $.when(
            ajaxRequest, // Returns $.Deferred()
            m.util.animateDeffered(swipeOutTarget, "fadeOutDown", true) // Returns $.Deferred()
        );
    };

    // Swipe and Show
    stage2 = function () {
        swipeInTarget.show();

        return m.util.animateDeffered(swipeInTarget, "fadeInDown"); // Returns $.Deferred()
    };

    finalStage = function () {
        dfd.resolve();
    }

    failStage = function () {
        console.log("fail!");
        swipeInTarget.hide();
    };

    functionPromise = stage1()
        .then(stage2)
        .then(finalStage);

    $.when(functionPromise,dfd)
        .fail(failStage);

    return dfd;
};

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

Я хочу иметь возможность отклонить цепочку в любой момент, сделав что-то вроде этот.

// called on script load.
var currentAction = $.Deferred();

// Called everytime someone starts animation chain.
currentAction.reject();
currentAction = m.util.genericSwipeVertFunc(dfd, swipeOutTarget, swipeInTarget);
            );

В моем текущем коде функция failFunction выполняется правильно, но она не останавливает выполнение stage2. Поэтому он скрывает, а затем показывает это и продолжает ломать вещи.

Итак, к вопросу. Как я могу поместить отложенное в цепочку, которую я могу отклонить в любое время во время выполнения цепочек ? :)


Пример скрипки http://jsfiddle.net/ff3jojbo/


Обновление для уточнения

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


Ответ скрипка http://jsfiddle.net/aefkwa8a/

2 2

2 ответа:

Попробуйте использовать .queue() , .promise()

// array of functions to add to queue
var arr = [];

var swipeInTarget = $("#stage1");

var swipeOutTarget = $("#stage2");

// pseudo `ajax` call
var ajaxRequest = function ajaxRequest(next) {
  return $.Deferred(function(d) {
    setTimeout(function() {
      d.resolve("ajaxRequest")
    }, Math.random() * 5000)
  }).promise()
  // Note `.then(function() {console.log(this)})` for example , 
  // can be removed
  .then(function(data) {
    console.log(data)
  }).then(next)
}

var stage1 = function stage1(next) {
  return swipeOutTarget.fadeTo(Math.random() * 5000, Math.random())
        .promise()
        // Note `.then(function() {console.log(this)})` for example , 
        // can be removed
        .then(function() {
          console.log(this)
        })
        .then(next)
}

var stage2 = function stage2(next) {
  return swipeInTarget
    .show(Math.random() * 5000, function() {
      return $(this).fadeTo(Math.random() * 2000, Math.random())
    })
    .promise()
     // Note `.then(function() {console.log(this)})` for example , 
     // can be removed
    .then(function() {
      console.log(this)
    })
    .then(next)
}
// do stuff when queue cleared
var failStage = function failStage() {
  return swipeInTarget.hide(Math.random() * 2000)
    .promise().then(function() {
      console.log("m processes stopped")
    })
}
// always do stuff when queue cleared,
// or all functions in queue complete
var finalStage = function finalStage() {
  console.log("complete", this)
}
// create jQuery object
var m = $({
  m: arr
});
// add function to `"stages"` queue
m.queue("stages", [stage1, stage2, finalStage]);
// do stuff when all functions complete , or queue cleared
m.promise("stages")
.then(finalStage);
// dequque `"stages"` queue
m.dequeue("stages");
// clear `"stages"` queue
$("button").click(function() {
  m.queue("stages", [])
  .promise("stages").always(failStage)
})
#stage2 {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button>stop m processes</button>
<div id="stage1">stage1</div>
<div id="stage2">stage2</div>

Собственное решение OPздесь может потерпеть неудачу после нескольких щелчков мыши. В частности, если кнопка нажата во время полета секции, то последняя требуемая секция может прилететь, а затем исчезнуть.

Это решение совершенно другое.

Вместо использования очереди / dequeue jQuery, он использует обычную цепочку stage1().then(stage2) обещаний и останавливает продвижение вниз по этой цепочке, удаляя классы анимации CSS из анимированного элемента и отсоединяя его обработчик animationend, таким образом обеспечивая обещание связанное с завершением никогда не разрешается.

Как вы увидите, большая часть функциональности учитывается как плагины jQuery, что обеспечивает удобный и компактный синтаксис.

$(function () {
    // **************************
    // *** Various outer vars ***
    // **************************
    var $sections = $('#TabSection>div').hide();
    var ajaxPromise;
    var str = {
        //various strings
        'animationend': 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend',
        'fadeOutClasses': 'fadeOutDown animated',
        'fadeInClasses': 'fadeInDown animated',
        'allClasses': 'fadeOutDown fadeInDown animated'
    };

    // ***********************************************
    // *** Utilities in the form of jQuery plugins ***
    // ***********************************************
    jQuery.fn.killAnim = function(animation) {
        /* jQuery plugin :
         * Remove all the animation classes from all possible targets, and
         * detach any currently attached animationend handlers.
         * Depends on: str (object).
         */
        return this.off(str.animationend).removeClass(str.allClasses);
    };
    jQuery.fn.cssAnimate = function (animation) {
        /* jQuery plugin :
         * Perform CSS animation and return promise.
         * Depends on: str (object); killAnim (plugin).
         */
        var that = this;
        return $.Deferred(function(dfd) {
            // if no target or target not visible, resolve;
            if(that.length == 0 || !that.is(':visible')) {
                dfd.resolve();
            }
            that.addClass(animation).one(str.animationend, dfd.resolve);
        }).then(function() {
            that.killAnim();
        });
    };
    jQuery.fn.genericSwipeVertFunc = function () {
        /* jQuery plugin :
         * Sequence two CSS animations - fadeOut then fadeIn.
         * Depends on: str (object); killAnim (plugin); cssAnimate (plugin).
         */
        var that = this; // swipeInTarget
        var swipeOutTarget = $sections.filter(':visible()').eq(0);
        function stage1() {
            $sections.killAnim().not(swipeOutTarget).hide();
            return swipeOutTarget.cssAnimate(str.fadeOutClasses).then(function() {
                swipeOutTarget.hide();
            });
        };
        function stage2() {
            $sections.not(that).killAnim().hide();
            return that.show().cssAnimate(str.fadeInClasses);
        };
        return stage1().then(stage2);
    };

    // **********************
    // *** Event handlers ***
    // **********************
    $('button').on('click', function (event) {
        var inTarget = $($(this).data('tar'));
        if(ajaxPromise) {
            ajaxPromise.abort('aborted');
        }
        // *** start: emulate AJAX ***
        ajaxPromise = $.Deferred(function(dfrd) {
            setTimeout(dfrd.resolve, 1000);
        });
        ajaxPromise.abort = ajaxPromise.reject;
        // *** end: emulate AJAX ***
        ajaxPromise.then(function() {
            return inTarget.genericSwipeVertFunc();
        }).fail(function(e) {
            $sections.killAnim().hide();
            console.log(e);
        });
    });
});
Я считаю это решение более надежным. Даже с большим количеством маниакальных щелчков, я не мог победить его.

Попробуйте здесь