Jak animować w jQuery bez układania wywołań zwrotnych?

Powiedzmy, że mam trzy divy i chciałbym, aby każdy z nich animował po zakończeniu poprzedniego. Obecnie piszę to:

$('div1').fadeOut('slow', function() {
    $('div2').fadeOut('slow', function() {
        $('div3').fadeOut('slow');
    });
});
Co jest brzydkie, ale do opanowania.

Teraz wyobraź sobie, że mam 10 różnych animacji, które muszą się odbywać jedna po drugiej na różnych elementach . Nagle kod staje się tak niezgrabny, że jest niezwykle trudny do zarządzania...

Oto pseudokod do tego, co chcę zrobić:

$('div1').fadeOut('slow' { delay_next_function_until_done: true } );
$('div2').fadeOut('slow' { delay_next_function_until_done: true } );
$('div3').animate({ top: 500 }, 1000 );

Jak to osiągnąć?

Author: gdoron, 2012-04-29

7 answers

Jeśli używasz najnowszej wersji jQuery, użyj obietnic animacji:

$('div1').fadeOut('slow').promise().pipe(function() {
    return $('div2').fadeOut('slow');
}).pipe(function() {
    return $('div3').animate({ top: 500 }, 1000 );
});

Możesz zrobić to generycznie:

$.chain = function() {
    var promise = $.Deferred().resolve().promise();
    jQuery.each( arguments, function() {
        promise = promise.pipe( this );
    });
    return promise;
};

var animations = $.chain(function() {
    return $('div1').fadeOut('slow');
}, function() {
    return $('div2').fadeOut('slow');
}, function() {
    return $('div3').animate({ top: 500 }, 1000 );
});

$.when( animations ).done(function() {
    // ALL ANIMATIONS HAVE BEEN DONE IN SEQUENCE
});

Wciąż jest wiele zamknięć funkcji, ale taka jest natura Javascript. Jednak jest to o wiele bardziej naturalne i o wiele bardziej elastyczne przy użyciu Deferreds / Promises, ponieważ unikasz wywołań zwrotnych "Incepcja".

 22
Author: Julian Aubourg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-26 15:21:02

Robię to, dzięki tej metodzie możesz umieścić wszystkie div, jak chcesz, tylko dodając lub usuwając elementy w liście var, możesz również zmienić ich kolejność i nie musisz się martwić o Czas opóźnienia.

var list = [ '#div1', '#div2', '...' ];
var i = 0;
function fade(cb) {
    if (i < list.length) {
        $(list[i]).fadeOut('slow', function() {
            i++;
            fade(cb);
        });
    } else {
        cb && cb();
    }
}
fade();

Możesz również dodać wywołanie zwrotne po zakończeniu procesu

fade(function(){alert('end')});

Demo

 4
Author: xgc1986,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-29 10:58:10

Kiedy funkcje kończące lub wywołania zwrotne są zagnieżdżane zbyt głęboko lub kod jest powtarzany w kółko, mam tendencję do myślenia o rozwiązaniu tabeli danych ze wspólną funkcją:

function fadeSequence(list) {
    var index = 0;
    function next() {
        if (index < list.length) {
            $(list[index++]).fadeOut(next);
    }
    next();
}

var fades = ["div1", "div2", "div3", "div4", "div5"];
fadeSequence(fades);

I, jeśli chcesz innego rodzaju animacji dla niektórych elementów, możesz utworzyć tablicę obiektów, które opisują, co każda kolejna animacja ma być. Możesz umieścić tyle szczegółów w tablicy obiektów, ile było potrzebne. Możesz nawet mieszać animacje z innymi synchroniczne wywołania metody jQuery w ten sposób:

function runSequence(list) {
    var index = 0;
    function next() {
        var item, obj, args;
        if (index < list.length) {
            item = list[index++];
            obj = $(item.sel);
            args = item.args.slice(0);
            if (item.sync) {
                obj[item.type].apply(obj, args);
                setTimeout(next, 1);
            } else {
                args.push(next);
                obj[item.type].apply(obj, args);
            }
        }
    }
    next();
}

// sequence of animation commands to run, one after the other
var commands = [
    {sel: "#div2", type: "animate", args: [{ width: 300}, 1000]},
    {sel: "#div2", type: "animate", args: [{ width: 25}, 1000]},
    {sel: "#div2", type: "fadeOut", args: ["slow"]},
    {sel: "#div3", type: "animate", args: [{ height: 300}, 1000]},
    {sel: "#div3", type: "animate", args: [{ height: 25}, 1000]},
    {sel: "#div3", type: "fadeOut", args: ["slow"]},
    {sel: "#div4", type: "fadeOut", args: ["slow"]},
    {sel: "#div1", type: "fadeOut", args: ["slow"]},
    {sel: "#div5", type: "css", args: ["position", "absolute"], sync: true},
    {sel: "#div5", type: "animate", args: [{ top: 500}, 1000]}
];
runSequence(commands);

A oto demo tej drugiej opcji: http://jsfiddle.net/jfriend00/PEVEh/

 3
Author: jfriend00,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-29 08:10:01

Jednym ze sposobów, aby to zrobić, byłoby napisanie własnej funkcji pomocniczej, w ten sposób:

$.fn.sequentialFade = function() {
    if(this.length > 0) {
        var $set = $(this);
        $set.eq(0).fadeOut(function() {
            $set.slice(1).sequentialFade();
        });
    }
}

I używaj go tak:

$('.div1, .div2. .div3').sequentialFade();

Http://jsfiddle.net/JpNgv/

 2
Author: Tatu Ulmanen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-29 07:01:57

Spróbuj czegoś takiego:

$( 'div1' ).fadeOut();
$( 'div2' ).delay( 500  ).fadeOut();
$( 'div3' ).delay( 1000 ).fadeOut();

Dostosuj czas w razie potrzeby

 1
Author: Mark Kahn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-29 06:53:03

Użyj tego:

$('#div1, #div2, #div3').each(function(index){
    $(this).delay(1000 * index).hide(1000);
});

Jeśli możesz dać <div>S klasę:

$('.forHide').each(function(index, value){
    $(this).delay(1000 * index).hide(1000);
});​
  • pierwszy element zanika po 1000 * 0 = od razu z animacją trwającą jedną sekundę.
  • drugi element zanika po 1000 * 1 = jedna sekunda z animacją jednej sekundy.
  • trzeci element zanika po 1000 * 2 = dwie sekundy z animacją jednej sekundy.
  • ...
  • ...
  • N element zanika po 1000 * N = N sekund z animacją trwającą jedną sekundę.

Live DEMO

 1
Author: gdoron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-29 07:19:04

Callback to przyjaciel, nie odpychaj go. Istnieją sposoby na ich uproszczenie. Oto jeden z nich

$('div1').fadeOut('slow', div2)
function div3() { $('div3').fadeOut('slow'); }
function div2() { $('div2').fadeOut('slow', div3); }
 1
Author: Starx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-29 08:34:21