2012-04-29 20 views
15

Digamos que tengo tres divs, y me gustaría que cada uno animara una vez que haya terminado el anterior. Actualmente, escribo esto:¿Cómo puedo animar en jQuery sin apilar devoluciones de llamada?

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

Cuál es feo, pero manejable.

Ahora imagine que tengo 10 animaciones diferentes que deben suceder una tras otra en diferentes elementos. De repente, el código se vuelve tan torpe que es extremadamente difícil de manejar ...

Aquí está pseudocódigo para lo que estoy buscando hacer:

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

¿Cómo lograr esto?

+0

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

Respuesta

22

Si está utilizando una versión reciente de jQuery, utilice el promesas de animación:

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

Usted puede hacer que sea genérica:

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

Todavía hay un montón de cierres de funciones, pero esa es la naturaleza misma de Javascript. Sin embargo, es mucho más natural y mucho más flexible con Deferreds/Promises ya que evita el inicio de las retrollamadas.

+0

Esto es todo! ¡Gracias! –

+0

Hermoso código. :) –

+0

Simple y limpio - muy bonito :) –

1

intentar algo como:

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

Ajustar el tiempo que sea necesario

+1

Parece una forma realmente torpe de hacer esto. Además, me imagino que la demora no garantiza que estos eventos se alineen, especialmente al construir una cadena de muchos eventos. – Brad

+0

@Brad - En realidad con jQuery sí. jQuery usa el tiempo entre el último fotograma y el actual, por lo tanto, a menos que el navegador se bloquee, solo se apagarán unos pocos ms. –

+0

Obtuvo 10 elementos. en lugar de escribirlo 10 veces, puedes hacerlo con '$ (...). each' y usar el índice para la demora. [mira esto] (http://stackoverflow.com/a/10370321/601179) – gdoron

1

Utilice esta:

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

Si usted puede dar la <div> es una clase:

$('.forHide').each(function(index, value){ 
    $(this).delay(1000 * index).hide(1000); 
});​ 
  • El primer elemento desaparece después de 1000 * 0 = de inmediato con una animación de un segundo.
  • El segundo elemento desaparece después de 1000 * 1 = Un segundo con una animación de un segundo.
  • El tercer elemento desaparece después de 1000 * 2 = Dos segundos con una animación de un segundo.
  • ...
  • ...
  • El elemento n desvanece en después de 1000 * n = n segundos con la animación de un segundo.

Live DEMO

+0

Pero esto no garantiza que el efecto se complete en 1000 milisegundos. – Starx

+0

@Starx. está garantizado ahora Echale un vistazo. – gdoron

+0

¿El selector de jquery garantiza que la orden de devolución será el orden en que coloque los elementos en el selector? Solo curiosidad ... – Endophage

1

devolución de llamada es un amigo, no te empuje en sentido contrario. Hay formas de simplificarlos.Aquí está uno de ellos

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

Una forma de hacer esto sería para escribir su propia función auxiliar, así:

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

y usarlo de esta manera:

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

http://jsfiddle.net/JpNgv/

+0

[No funciona] (http://jsfiddle.net/RwMy7/7/) – gdoron

+0

It [do now] (http://jsfiddle.net/JpNgv/). –

+1

Nota: esto los desvanecerá en orden de secuencia DOM, no necesariamente en el orden en que los incluye porque los objetos jQuery ordenan sus artículos en orden DOM. – jfriend00

4

Lo hago, con este método puedes poner todos los divs como quieras, solo agregando o eliminando elementos en la lista var, también puedes reordenarlos y no tienes que preocuparte por el tiempo de demora.

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

también se puede añadir una devolución de llamada cuando el proceso termina

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

Demo

+0

@ jfriend00 tu código tiene un error con la opción de sincronización, en el momento en que un elemento tiene sincronización lanzarás otra secuencia a la vez, te pongo un ejemplo para entenderlo 'list = [0-> sync, 1- > nosync, 2-> sincronización, 3-> nosync, 4-> nosync, 5-> nosync, 6-> nosync, 7-> sincronización] ' ejecutará ' 0 1 (porque 0 tiene sincronización)// 2 (arrojado por 0), 3 (arrojado por 1), 4 (porque o2 tiene sincronización) // 5 (arrojado por 2), 6 (arrojado por 3), 7 (arrojado por 4) ' y me propuse que la ejecución correcta será '0 1 // 2 3 // 4 // 5 // 6 // 7' – xgc1986

+0

La opción de sincronización requiere que identifique correctamente las operaciones sychronous y las operaciones asíncronas. Por lo tanto, '.css()' es 'sync: true' y todas las animaciones no lo son. Aparte de eso, no estoy seguro de qué "error" estás hablando. Si se tratara de un código de producción, podría tener una lista de todos los métodos de animación conocidos y detectarlos de forma automática, pero no creí que ese valor fuera apropiado en mi respuesta. Además, ¿por qué pones esto como un comentario sobre tu respuesta, no mía (casi no vi tu comentario debido a eso)? – jfriend00

+0

pero después de una operación con sincronización, lanzará dos operaciones de forma síncrona, pero si pone la siguiente sincronización de operación: falsa, esto será ignorado y continuará lanzando las dos operaciones siguientes, si agrega otro comando de sincronización, entonces será 3 operaciones a la vez y continúa – xgc1986

3

Cuando funciones o devoluciones de llamada de finalización vez tienes anidados demasiado profundo o código está siendo repetido una y otra vez, tienden a pensar en una solución de tabla de datos con una función común:

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

Y, si deseaba un tipo diferente de animación para algunos de los elementos, podría crear una matriz de objetos que describan qué se supone que es cada animación sucesiva. Podría poner tantos detalles en la matriz de objetos como sea necesario. Puede incluso animaciones entremezclados con otro método jQuery sincrónica llamadas como esta:

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

Y, esto es una demostración de trabajo de esta segunda opción: http://jsfiddle.net/jfriend00/PEVEh/

+1

LOL, pensé que el concepto era hacerlo simple – Starx

+2

La primera opción es muy simple. El segundo es reutilizable para cualquier secuencia de animación en tantos objetos como desee sin escribir ningún código nuevo cada vez. Usted acaba de construir una tabla de animación. Si prefiere codificar manualmente la secuencia que muestro en el segundo, siéntase libre, pero el OP preguntó cómo hacerlo sin todas las devoluciones de llamada anidadas, así que escribí un motor genérico que lo manejaría. También tenga en cuenta que el OP preguntó cómo hacer que funcione con 10 o más animaciones secuenciales y diferentes en diferentes objetos. No veo ninguna otra solución que aborde esa parte de la pregunta. – jfriend00

Cuestiones relacionadas