2012-04-29 36 views
15

Załóżmy, że mam trzy divy i chciałbym, aby każdy animował się po wykonaniu poprzedniego. Obecnie piszę:Jak animować w jQuery bez łączenia zwrotnego?

$('div1').fadeOut('slow', function() { 
    $('div2').fadeOut('slow', function() { 
     $('div3').fadeOut('slow'); 
    }); 
}); 

Co jest brzydkie, ale łatwe w zarządzaniu.

Teraz wyobraź sobie, że mam 10 różnych animacji, które muszą się zdarzyć jedna po drugiej na różnych elementach. Nagle kod dostaje tak niezgrabne, że jest to niezwykle trudne do zarządzania ...

Oto pseudokod dla czego szukam 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ąć?

+0

http://cdmckay.org/blog/2010/06/22/how-to-use-custom-jquery-animation-queues/ – Brad

Odpowiedz

22

Jeśli używasz najnowszej wersji jQuery, użyj obietnice animacja:

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

można zrobić to ogólna:

$.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 
}); 

Nadal wiele funkcji zamknięć, ale to jest bardzo charakter JavaScript. Jest jednak o wiele bardziej naturalny i bardziej elastyczny przy użyciu Odroczonych/Obietnic, ponieważ unikasz "wywoływania" wywołań zwrotnych.

+0

To jest to! Dziękuję Ci! –

+0

Piękny kod. :) –

+0

Prosto i czysto - bardzo ładnie :) –

1

spróbować czegoś takiego:

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

Regulacja taktowania jako niezbędnego

+1

Wydaje się, że to bardzo chytry sposób. Dodatkowo, wyobrażam sobie, że opóźnienie nie gwarantuje, że te wydarzenia będą się zgadzały, zwłaszcza gdy buduje się łańcuch wielu wydarzeń. – Brad

+0

@Brad - w rzeczywistości z jQuery to robi. jQuery używa czasu między ostatnią klatką a bieżącą, więc dopóki przeglądarka nie zablokuje, będą wyłączone tylko o kilka ms. –

+0

Dostał 10 elementów. zamiast pisać 10 razy, możesz to zrobić za pomocą '$ (...). each' i użyć indeksu dla opóźnienia. [sprawdź to] (http://stackoverflow.com/a/10370321/601179) – gdoron

1

użyj:

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

Jeśli można dać <div> s klasa:

$('.forHide').each(function(index, value){ 
    $(this).delay(1000 * index).hide(1000); 
});​ 
  • Pierwszy element zanika po 1000 * 0 = natychmiast 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.
  • ...
  • ...
  • Element n wyłania się po 1000 * n = n sekund animacji jednej sekundy.

Live DEMO

+0

Ale to nie może zagwarantować, że efekt zostanie ukończony w ciągu 1000 milisekund. – Starx

+0

@Starx. jest to teraz gwarantowane. Sprawdź to. – gdoron

+0

Czy selektor jquery gwarantuje, że zamówienie powrotu będzie kolejnością elementów w selektorze? Po prostu ciekawy ... – Endophage

1

oddzwaniania jest przyjacielem, nie naciskać go. Są sposoby na ich uproszczenie.Oto jeden z nich

$('div1').fadeOut('slow', div2) 
function div3() { $('div3').fadeOut('slow'); } 
function div2() { $('div2').fadeOut('slow', div3); } 
2

Jednym ze sposobów, aby to zrobić byłoby napisać własną funkcję pomocnika, tak:

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

i używać go tak:

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

http://jsfiddle.net/JpNgv/

+0

To [nie działa] (http://jsfiddle.net/RwMy7/7/) – gdoron

+0

To [robi teraz] (http://jsfiddle.net/JpNgv/). –

+1

Uwaga: spowoduje to zanikanie ich w kolejności sekwencji DOM, niekoniecznie w kolejności, w jakiej zostały podane, ponieważ obiekty jQuery zamawiają swoje pozycje w kolejności DOM. – jfriend00

4

Robię to, za pomocą tej metody możesz umieścić wszystkie elementy div tak, jak chcesz, tylko dodając lub usuwając elementy z listy var, możesz również zmienić ich kolejność i nie musisz się martwić opóźnieniem.

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żna również dodać do wywołania zwrotnego, gdy proces kończy

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

Demo

+0

@ jfriend00 Twój kod ma błąd z opcją synchronizacji, w momencie, gdy element ma synchronizację, wyrzucisz kolejną sekwencję naraz, podaję ci przykład, aby go anulować 'list = [0-> sync, 1- > nosync, 2-> sync, 3> nosync, 4-> nosync, 5-> nosync, 6-> nosync, 7-> sync] ' będzie działać ' 0 1 (ponieważ 0 ma synchronizację)// 2 (rzucane przez 0), 3 (rzucane przez 1), 4 (ponieważ o2 ma synchronizację) // 5 (rzucane przez 2), 6 (rzucane przez 3), 7 (rzucane przez 4) ' oraz i suposed poprawny przebieg będzie '0 1 // 2 3 // 4 // 5 // 6 // 7' – xgc1986

+0

Opcja synchronizacji wymaga prawidłowej identyfikacji operacji synchronicznych i operacji asynchronicznych. Zatem '.css()' to 'sync: true', a wszystkie animacje nie są. Poza tym nie jestem pewien, o czym mówisz "robakiem". Gdyby to był kod produkcyjny, mógłby mieć listę wszystkich znanych metod animacji i wykryć je autoamtycznie, ale nie sądziłem, że w mojej odpowiedzi odpowiedni byłby stosunek masy. Poza tym, dlaczego umieściłeś to jako komentarz do twojej odpowiedzi, a nie mojej (prawie nie widziałem twojego komentarza z tego powodu)? – jfriend00

+0

, ale po operacji z synchronizacją, wyrzucisz dwie operacje synchronicznie, ale jeśli położysz następną operację synchronizacji: false, to zostanie zignorowane i będziesz kontynuować rzucanie następnych dwóch operacji, jeśli dodasz kolejne polecenie synchronizacji, to będzie 3 operacje na raz i ciąg dalszy – xgc1986

3

gdy kiedykolwiek funkcje zakończeniu lub callbacks się zagnieżdżone zbyt głęboko lub kod jest uzyskiwanie powtarzane w kółko, I mają tendencję do myślenia o rozwiązaniu z tablicą danych o wspólnej funkcji:

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); 

Jeśli chcesz uzyskać inny typ animacji dla niektórych elementów, możesz utworzyć tablicę obiektów opisujących, czym powinna być każda kolejna animacja. Możesz umieścić tyle szczegółów w tablicy obiektów, ile potrzeba. Można nawet INTERMIX animacje z drugiej synchronicznej metody jQuery zwraca tak:

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 działa od tej drugiej opcji: http://jsfiddle.net/jfriend00/PEVEh/

+1

LOL, Myślałem, że koncepcja jest prosta. – Starx

+2

Pierwsza opcja jest bardzo prosta. Drugi to możliwość wielokrotnego użytku dla dowolnej sekwencji animacji w dowolnej liczbie obiektów bez pisania za każdym razem nowego kodu. Po prostu budujesz tabelę animacji. Jeśli wolisz kodować sekwencję, którą pokazuję w drugiej ręcznie, nie krępuj się, ale OP zapytał, jak to zrobić bez wszystkich zagnieżdżonych wywołań zwrotnych, więc napisałem ogólny silnik, który by sobie z tym poradził. Pamiętaj też, że OP zadał pytanie, jak sprawić, by działał z 10 lub więcej sekwencyjnymi i różnymi animacjami na różnych obiektach. Nie widzę żadnych innych rozwiązań, które rozwiązałyby tę część pytania. – jfriend00