2012-04-29 2 views
15

Предположим, у меня есть три div, и я бы хотел, чтобы каждый из них был анимационным, как только предыдущий закончен. В настоящее время я пишу это:Как сделать анимацию в jQuery без укладки обратных вызовов?

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

Какая уродливая, но управляемая.

Теперь представьте, что у меня есть 10 различных анимаций, которые должны произойти один за другим на разных элементах. Внезапно код становится настолько неуклюжим, что это очень трудно управлять ...

Вот псевдокод для того, что я ищу, чтобы сделать:

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

Как этого добиться?

+0

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

ответ

22

Если вы используете последнюю версию JQuery, используйте анимация обещает:

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

Вы можете сделать это родовое:

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

Все еще много закрытий функций, но это сама природа Javascript. Тем не менее, это намного более естественно и намного более гибко, используя Отсрочки/Обещания, поскольку вы избегаете «создания» обратных вызовов.

+0

Вот оно! Спасибо! –

+0

Красивый код. :) –

+0

Простой и чистый - очень приятно :) –

1

попробовать что-то вроде:

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

Настройка времени по мере необходимости

+1

Это похоже на действительно неуклюжий способ сделать это. Кроме того, я бы предположил, что задержка не гарантирует выравнивания этих событий, особенно при создании цепочки многих событий. – Brad

+0

@Brad - На самом деле с jQuery он делает. jQuery, используя синхронизацию между последним кадром и текущим, поэтому, если браузер не блокируется, они будут отключены только на несколько мс. –

+0

Он получил 10 элементов. вместо того, чтобы писать его 10 раз, вы можете сделать это с помощью '$ (...). each' и использовать индекс для задержки. [проверьте это] (http://stackoverflow.com/a/10370321/601179) – gdoron

1

Используйте это:

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

Если вы можете дать <div> S класс:

$('.forHide').each(function(index, value){ 
    $(this).delay(1000 * index).hide(1000); 
});​ 
  • Первый элемент исчезает после 1000 * 0 = сразу с анимацией одной секунды.
  • Второй элемент исчезает после 1000 * 1 = Одна секунда с анимацией одной секунды.
  • Третий элемент исчезает после 1000 * 2 = Две секунды с анимацией в одну секунду.
  • ...
  • ...
  • п элемент выцветает после 1000 * N = п секунд анимации в одну секунду.

Live DEMO

+0

Но это не может гарантировать, что эффект будет завершен за 1000 миллисекунд. – Starx

+0

@Starx. теперь это гарантировано. проверьте это. – gdoron

+0

Является ли селектор jquery гарантией возврата заказа, будет ли порядок, в который вы помещаете элементы в селектор? Просто любопытно ... – Endophage

1

Обратный вызов является другом, то не оттолкнуть ее. Есть способы упростить их.Вот один из них

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

Один из способов сделать это было бы написать свою собственную функцию помощника, например, так:

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

И использовать его так:

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

http://jsfiddle.net/JpNgv/

+0

Это [не работает] (http://jsfiddle.net/RwMy7/7/) – gdoron

+0

Это [сейчас] (http://jsfiddle.net/JpNgv/). –

+1

Примечание: это приведет к их исчезновению в порядке последовательности DOM, не обязательно в том порядке, в котором вы их перечисляете, поскольку объекты jQuery упорядочивают свои объекты в порядке DOM. – jfriend00

4

Я делаю это, используя этот метод, вы можете поместить все div, как хотите, только добавляя или удаляя элементы в списке var, также вы можете их переупорядочить, и вам не нужно беспокоиться о времени задержки.

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

вы также можете добавить функцию обратного вызова, когда процесс завершается

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

Demo

+0

@ jfriend00 ваш код имеет ошибку с опцией синхронизации, на данный момент у элемента есть синхронизация, вы будете генерировать другую последовательность за раз, я поставлю вам пример, чтобы раскрыть его 'list = [0-> sync, 1- > nosync, 2-> sync, 3-> nosync, 4-> nosync, 5-> nosync, 6-> nosync, 7-> sync] ' он будет работать ' 0 1 (поскольку 0 имеет синхронизацию)// 2 (выбрасывается на 0), 3 (бросается на 1), 4 (потому что o2 имеет синхронизацию) // 5 (выбрано 2), 6 (выбрано 3), 7 (выбрано 4) ' и i что правильный запуск будет '0 1 // 2 3 // 4 // 5 // 6 // 7' – xgc1986

+0

Параметр синхронизации требует, чтобы вы правильно идентифицировали синхронные операции и асинхронные операции. Таким образом, '.css()' is 'sync: true', и все анимации - нет. Кроме этого, я не уверен, о какой «ошибке» вы говорите. Если это был производственный код, он мог бы иметь список всех известных методов анимации и обнаруживать их автоматически, но я не думал, что heft был уместным в моем ответе. Кроме того, почему вы поставили это как комментарий к своему ответу, а не мой (из-за этого я почти не заметил вашего комментария)? – jfriend00

+0

, но после операции с синхронизацией вы будете выполнять две операции синхронно, но если вы поместите следующую операцию sync: false, это будет игнорироваться, и вы продолжите выполнение следующих двух операций, если вы добавите еще одну команду синхронизации, тогда будет 3 операции за один раз и перейти на – xgc1986

3

Когда когда-либо функции завершения или обратные вызовы получить вложенной слишком глубоко или код становится повторяется снова и снова, я имеют тенденцию думать о решении таблицы данных с общей функцией:

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

И, если вам нужен анимация другого типа для некоторых элементов, вы можете создать массив объектов, которые описывают, какими должны быть каждая последующая анимация. Вы можете добавить столько деталей в массив объектов, сколько было необходимо. Вы даже можете смешивать анимации с другой синхронным методом JQuery называет так:

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

И вот рабочий пример этого второго варианта: http://jsfiddle.net/jfriend00/PEVEh/

+1

LOL, я думал, что концепция должна была сделать его простым. – Starx

+2

Первый вариант очень прост. Второй вариант можно использовать повторно для любой последовательности анимации по так много объектов, сколько вы хотите, не записывая никакого нового кода каждый раз. Вы просто создаете таблицу анимации. Если вы предпочитаете кодировать последовательность, которую я показываю во второй вручную, не стесняйтесь, но ОП спросил, как это сделать без всех вложенных обратных вызовов, поэтому я написал универсальный движок, который будет обрабатывать его. Также имейте в виду, что ОП задал вопрос, как заставить его работать с 10 или более последовательными и разными анимациями на разных объектах. Я не вижу никаких других решений, которые затрагивают эту часть вопроса. – jfriend00

Смежные вопросы