2012-04-29 13 views
15

Diciamo che ho tre div e mi piacerebbe che ciascuno animasse una volta terminato il precedente. Attualmente, scrivo questo:Come faccio ad animare in jQuery senza impilare i callback?

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

Che è brutto, ma gestibile.

Ora immagino di avere 10 diverse animazioni che devono accadere una dopo l'altra su diversi elementi. Improvvisamente il codice diventa così goffo che è estremamente difficile da gestire ...

Ecco pseudocodice per quello che sto cercando di fare:

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

Come posso raggiungere questo obiettivo?

+0

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

risposta

22

Se stai usando una versione recente di jQuery, utilizzare il promesse di animazione:

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

si può rendere generico:

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

Ancora molte chiusure di funzioni, ma questa è la natura stessa di Javascript. Tuttavia, è molto più naturale e molto più flessibile usando Deferred/Promises dato che si evitano i callback "inception".

+0

Questo è tutto! Grazie! –

+0

Bellissimo codice. :) –

+0

Semplice e pulito - molto bello :) –

1

provare qualcosa di simile:

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

Regolare i tempi, se necessario

+1

Sembra un modo davvero goffo per farlo. Inoltre, immagino che il ritardo non garantisca che questi eventi si allineino, specialmente quando si crea una catena di molti eventi. – Brad

+0

@Brad - In realtà con jQuery lo fa. jQuery utilizza il tempo tra l'ultimo fotogramma e quello corrente, quindi a meno che il browser non si blocchi, saranno spenti solo di pochi ms. –

+0

Ha 10 elementi. invece di scriverlo 10 volte, puoi farlo con '$ (...). each' e usare l'indice per il ritardo. [guarda questo] (http://stackoverflow.com/a/10370321/601179) – gdoron

1

Utilizzare questa:

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

Se si può dare la <div> s una classe:

$('.forHide').each(function(index, value){ 
    $(this).delay(1000 * index).hide(1000); 
});​ 
  • Il primo elemento scompare dopo 1000 * 0 = subito con animazione di un secondo.
  • Il secondo elemento scompare dopo 1000 * 1 = Un secondo con animazione di un secondo.
  • Il terzo elemento scompare dopo 1000 * 2 = Due secondi con animazione di un secondo.
  • ...
  • ...
  • L'elemento n svanisce in dopo 1000 * n = n secondi con animazione di un secondo.

Live DEMO

+0

Tuttavia, questo non può garantire che l'effetto venga completato in 1000 millisecondi. – Starx

+0

@Starx. ora è garantito. controlla. – gdoron

+0

Il selettore jquery garantisce che l'ordine di reso sarà l'ordine in cui inserirai gli elementi nel selettore? Solo curioso ... – Endophage

1

callback è un amico, Non spingerlo via. Ci sono modi per semplificarli.Ecco uno di loro

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

Un modo per farlo sarebbe quello di scrivere la propria funzione di supporto, in questo modo:

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

e usarlo in questo modo:

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

http://jsfiddle.net/JpNgv/

+0

Esso [non funziona] (http://jsfiddle.net/RwMy7/7/) – gdoron

+0

Esso [fa ora] (http://jsfiddle.net/JpNgv/). –

+1

Nota: questo li dissolverà nell'ordine di sequenza DOM, non necessariamente nell'ordine in cui vengono elencati perché gli oggetti jQuery ordinano i loro articoli nell'ordine DOM. – jfriend00

4

Lo faccio, con questo metodo puoi mettere tutti i div che vuoi, solo aggiungendo o eliminando elementi nella lista var, puoi anche riordinarli e non devi preoccuparti del tempo di ritardo.

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

è anche possibile aggiungere una richiamata quando il processo termina

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

Demo

+0

@ jfriend00 il tuo codice ha un bug con l'opzione di sincronizzazione, nel momento in cui un elemento ha sincronizzato getti un'altra sequenza alla volta, ti metto un esempio per comprenderlo 'list = [0-> sync, 1- > nosync, 4-> sync, 3-> nosync, 4-> nosync, 5-> nosync, 6-> nosync, 7-> sync] ' eseguirà ' 0 1 (perché 0 ha sincronizzazione)// 2 (lanciato da 0), 3 (lanciato da 1), 4 (perché o2 è sincronizzato) // 5 (lanciato da 2), 6 (lanciato da 3), 7 (lanciato da 4) ' e i suposed che la corsa corretta sarà '0 1 // 2 3 // 4 // 5 // 6 // 7' – xgc1986

+0

L'opzione di sincronizzazione richiede l'identificazione corretta delle operazioni sincrone e delle operazioni asincrone. Quindi '.css()' è 'sync: true' e tutte le animazioni no. Oltre a questo, non sono sicuro di quale "bug" tu stia parlando. Se si trattava di codice di produzione, poteva avere un elenco di tutti i metodi di animazione noti e rilevarli in modo automatico, ma non pensavo che il peso fosse appropriato nella mia risposta. Inoltre, perché hai messo questo come commento alla tua risposta, non mia (non ho quasi visto il tuo commento per quello)? – jfriend00

+0

ma dopo un'operazione con sincronizzazione, si lanciano due operazioni in modo sincrono, ma se si inserisce la sincronizzazione dell'operazione successiva: false, questo verrà ignorato e si continueranno a lanciare le due operazioni successive, se si aggiunge un altro comando di sincronizzazione, saranno 3 operazioni alla volta e vai su – xgc1986

3

Quando le funzioni di completamento di sempre o callback vengono annidati troppo in profondità o il codice sta ottenendo ripetuto più e più volte, ho tendono a pensare a una soluzione di tabella di dati con una funzione comune:

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

E, se si desidera un diverso tipo di animazione per alcuni elementi, è possibile creare una serie di oggetti che descrivono ciò che ogni animazione successiva dovrebbe essere. È possibile inserire tutti i dettagli nell'array di oggetti necessari. Si può anche animazioni Uso combinato con altro metodo di jQuery sincrono chiamate in questo modo:

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

Ed, ecco una demo funzionante di questa seconda opzione: http://jsfiddle.net/jfriend00/PEVEh/

+1

LOL, ho pensato che il concetto fosse quello di renderlo semplice – Starx

+2

La prima opzione è molto semplice. Il secondo è riutilizzabile per qualsiasi sequenza di animazione su tutti gli oggetti che vuoi senza dover scrivere ogni volta un nuovo codice. Hai appena creato una tabella di animazione. Se preferisci codificare manualmente la sequenza che mostro nella seconda, sentiti libero, ma l'OP ha chiesto come farlo senza tutti i callback annidati, così ho scritto un motore generico che lo avrebbe gestito. Inoltre, tieni presente che l'OP ha chiesto come farlo funzionare con 10 o più animazioni sequenziali e diverse su oggetti diversi. Non vedo altre soluzioni che risolvano quella parte della domanda. – jfriend00

Problemi correlati