Как связать или поставить в очередь пользовательские функции с помощью JQuery?

У меня есть несколько функций, которые выполняют разные анимации для разных частей HTML. Я хотел бы связать или поставить в очередь эти функции, чтобы они запускали анимацию последовательно, а не одновременно.

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

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

Помогает ли "очередь" jquery? Я не мог понять документацию для очереди.

Пример, JQuery:

 function One() {
        $('div#animateTest1').animate({ left: '+=200' }, 2000);
    }
    function Two() {
        $('div#animateTest2').animate({ width: '+=200' }, 2000);
    }

// Call these functions sequentially so that the animations
// in One() run b/f the animations in Two()
    One();
    Two();

HTML:

    <div id="animatetest" style="width:50px;height:50px;background-color:Aqua;position:absolute;"></div>
    <div id="animatetest2" style="width:50px;height:50px;background-color:red;position:absolute;top:100px;"></div>

Спасибо.

РЕДАКТИРОВАТЬ: я попробовал это с таймерами, но я подумал, что есть лучший способ сделать это.

РЕДАКТИРОВАТЬ № 2:

Позвольте мне быть более конкретным. У меня есть несколько функций, связанных с событиями щелчка и наведения на разные элементы страницы. Обычно эти функции не имеют ничего общего друг с другом (они не ссылаются друг на друга). Я хотел бы имитировать пользователя, проходящего через эти события, без изменения кода моих существующих функций.


person orandov    schedule 07.07.2009    source источник
comment
Я думаю, что использование функции обратного вызова, предоставляемой функцией анимации, — это способ сделать это.   -  person seth    schedule 08.07.2009
comment
Теперь есть способ сделать это в jquery, используя систему обещаний. Пожалуйста, проверьте мой ответ здесь: stackoverflow.com/a/18216474/726239   -  person Shaunak    schedule 13.08.2013


Ответы (14)


Код jQuery Queue не так хорошо документирован, как мог бы быть, но основная идея такова:

$("#some_element").queue("namedQueue", function() {
  console.log("First");
  var self = this;
  setTimeout(function() {
    $(self).dequeue("namedQueue");
  }, 1000);
});

$("#some_element").queue("namedQueue", function() {
  console.log("Second");
  var self = this;
  setTimeout(function() {
    $(self).dequeue("namedQueue");
  }, 1000);
});

$("#some_element").queue("namedQueue", function() {
  console.log("Third");
});

$("#some_element").dequeue("namedQueue");

Если вы запустите этот код, вы увидите «Первый» в консоли, паузу в 1 с, увидите «Второй» в консоли, еще одну паузу в 1 с и, наконец, увидите «Третий» в консоли.

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

Все анимации в jQuery запускаются внутри очереди с именем fx. Когда вы анимируете элемент, он автоматически добавляет новую анимацию в очередь и вызывает удаление из очереди, если в данный момент анимации не выполняются. Вы можете вставить свои собственные обратные вызовы в очередь fx, если хотите; вам просто нужно будет вызвать dequeue вручную в конце (как и при любом другом использовании очереди).

person Yehuda Katz    schedule 08.07.2009
comment
$('div#myDiv1').queue('fx',fn1); $('div#myDiv2').queue('fx',fn2); Обратите внимание, что они выполняются параллельно, а не последовательно. Поэтому, если у вас есть две функции с animate() для разных элементов, они также будут выполняться параллельно. Таким образом, простое использование очередей не позволит выполнять последовательное выполнение. - person user120242; 08.07.2009
comment
Если вы используете глобальный объект, а не отдельные селекторы перед .queue, это - person methodin; 20.11.2010
comment
просто для связывания функций с эффектами мы можем использовать систему промисов, которая реализована в моем .promise() в jQuery. Проверьте ответ здесь, например: stackoverflow.com/a/18216474/726239 - person Shaunak; 13.08.2013

Я бы создал массив функций и добавил к нему каждую функцию, которую вы хотите поставить в очередь.

Затем я бы добавил вызов функции, который перебирает массив и вызывает каждую функцию для события через jQuery.

Вероятно, вы могли бы создать очень простой плагин для jQuery, который также мог бы обрабатывать это внутри.

person Spencer Ruport    schedule 07.07.2009
comment
Я думаю, что ваша идея сломается, если функция, которую нужно поставить в очередь, является асинхронной, и если автор хочет дождаться, пока асинхронная функция будет ждать, прежде чем запускать следующую функцию в списке. - person SolutionYogi; 08.07.2009

Предполагая, что вы хотите сохранить One и Two как отдельные функции, вы можете сделать что-то вроде этого:

function One(callback) {
    $('div#animateTest1').animate({ left: '+=200' }, 2000, 
        function(e) { callback(); });
}
function Two() {
    $('div#animateTest2').animate({ width: '+=200' }, 2000);
}

// Call these functions sequentially so that the animations
// in One() run b/f the animations in Two()
    One(Two);
person marcc    schedule 07.07.2009
comment
Что произойдет, если переданный параметр обратного вызова будет «нулевым»? Будет ли это работать? - person orandov; 08.07.2009
comment
Нет, вы должны сделать там некоторую обработку ошибок. Я написал это в поле редактирования SO, не особо задумывался об обработке ошибок или попытке запустить его. - person marcc; 08.07.2009
comment
Я не пытался критиковать ваши навыки обработки ошибок. :) Я спрашивал b/c, когда функции будут вызываться реальным пользователем, на самом деле щелкнув ссылку, не должно быть обратного вызова (null). Только когда код автоматизирует шаги, возникнет необходимость в обратном вызове. - person orandov; 08.07.2009
comment
Я знаю, что ему уже много лет, но отредактировал этот ответ, чтобы добавить в код некоторую обработку ошибок только для тех, кто сталкивается с этим и задается вопросом, как это сделать, при условии, что я не напортачил, набрав его;) - person andyface; 09.05.2014

В одном из моих веб-проектов мне приходилось выполнять различные последовательности действий в зависимости от того, какое событие сработало. Я сделал это с помощью обратных вызовов (что-то вроде шаблона «Посетитель»). Я создал объект для переноса любой функции или группы действий. Затем я бы поставил эти обертки в очередь; массив работает нормально. Когда событие срабатывало, я перебирал массив, вызывая специализированный метод моего объекта, который принимал обратный вызов. Этот обратный вызов вызвал продолжение моей итерации.

Чтобы обернуть функцию, вам необходимо знать применить функцию. . Функция применения принимает объект для области видимости и массив аргументов. Таким образом, вам не нужно ничего знать о функциях, которые вы оборачиваете.

person geowa4    schedule 07.07.2009

Я бы запустил второй как функцию обратного вызова:

$('div#animateTest1').animate({ left: '+=200' }, 2000, function(){
    two();
});

который будет запускать two() после завершения первой анимации, если у вас есть больше анимаций в очереди по времени, для таких случаев я использую jquery плагин таймера вместо setTimeout(), который в некоторых случаях очень удобен.

person Sinan    schedule 07.07.2009
comment
что не так с setTimeout? и как этот плагин делает это лучше, если внутри он использует указанный setTimeout? - person redsquare; 08.07.2009
comment
@redsquare $.animate уже создает таймер, так почему бы не оставить код сухим и не использовать его? Не нужно делать несколько таймеров. - person Jake Bellacera; 08.03.2012

Как насчет чего-то подобного?

var f1 = function() {return $(SELECTOR1).animate({ 'prop': 'value' }, 1000)};
var f2 = function() {return $(SELECTOR2).animate({ 'prop': 'value' }, 1000)};
var f3 = function() {return $(SELECTOR3).animate({ 'prop': 'value' }, 1000)};

$.when(f1).then(f2).then(f3);
person Skaumo    schedule 20.07.2014

Более безопасный и 100% рабочий способ — использовать переменную и if-ветки. В приведенном ниже примере мы выполняем 4 задания, которые занимают 1 секунду, после задания мы хотим запустить функцию f2.

<html><body>
<div id="a" class="a" />
 <script type="text/javascript">
var jobs = 0;
function f1() {
job(); 
job();
job();
job();    
};

function f2() {
    if(jobs >3)
        l("f2");
};

<!------------------------- ->
function job() {
    setTimeout(function() {
        l("j");
        jobs+=1;
        f2();
    }, 1000);
}
function l(a) {
    document.getElementById('a').innerHTML+=a+"<br />";
};
f1();


</script></body></html>
person gamadrillen    schedule 23.05.2011

Действительно простое исправление.

function One() {
        $('div#animateTest1').animate({ left: '+=200' }, 2000, Two);
    }
    function Two() {
        $('div#animateTest2').animate({ width: '+=200' }, 2000);
    }

// Call these functions sequentially so that the animations
// in One() run b/f the animations in Two()
    One();
//We no longer need to call Two, as it will be called when 1 is completed.
    //Two();
person DigitalDesignDj    schedule 11.01.2012

Поскольку интервал анимации уже определен в 2000 мс, вы можете сделать второй вызов с задержкой в ​​2000 мс:

One();
SetTimeout(function(){
  Two();
}, 2000);
person Alex Pavlov    schedule 07.07.2009
comment
Я знаю, что могу использовать таймеры или setTimeout, но я думал, что есть какая-то очередь, в которую я мог бы поместить свои функции. - person orandov; 08.07.2009
comment
... и не беспокойтесь о времени. - person trusktr; 06.03.2011

Следующее будет работать и не будет ошибкой, если обратный вызов равен нулю:

function One(callback)
{
    $('div#animateTest1').animate({ left: '+=200' }, 2000, 
       function()
       {
            if(callback != null)
            {
                 callback();
            }
       }
    );
}
function Two()
{
    $('div#animateTest2').animate({ width: '+=200' }, 2000);
}

var callback = function(){ Two(); };
One(callback);
person TrippRitter    schedule 27.11.2009

@TrippRitter должен прикрепить .call к обратному вызову

One = function(callback){
    $('div#animateTest1').animate({ left: '+=200' }, 2000, function(){
        if(typeof callback == 'function'){
            callback.call(this);
        }
    });
}

Two = function(){
    $('div#animateTest2').animate({ width: '+=200' }, 2000);
}

One(function(){ 
    Two();
});

но это то же самое, что делать следующее

$('div#animateTest1')
    .animate({ left: '+=200' }, 2000, 
    function(){
       Two();
    });

Two = function(){
    $('div#animateTest2').animate({ width: '+=200' }, 2000);
}
person twenjamin    schedule 30.11.2009

Копирование и вставка ............ 9 июля 2013 г.

<!doctype html>
<html lang="en">
    enter code here<head>
  <meta charset="utf-8">
  <title>jQuery.queue demo</title>
  <style>
  div { margin:3px; width:40px; height:40px;
        position:absolute; left:0px; top:30px;
        background:green; display:none; }
  div.newcolor { background:blue; }
    </style>
  <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>

  <button id="start">Start</button>
  <button id="stop">Stop</button>
  <div></div>
<script>
   $("#start").click(function () {
      $("div").show("slow");
      $("div").animate({left:'+=200'},5000);
      jQuery.queue( $("div")[0], "fx", function () {
        $(this).addClass("newcolor");
        jQuery.dequeue( this );
      });
      $("div").animate({left:'-=200'},1500);
      jQuery.queue( $("div")[0], "fx", function () {
        $(this).removeClass("newcolor");
        jQuery.dequeue( this );
      });
      $("div").slideUp();
    });
    $("#stop").click(function () {
      jQuery.queue( $("div")[0], "fx", [] );
      $("div").stop();
    });
</script>

</body>
</html>
person Jacques    schedule 06.09.2013

Хотя ответ Иегуды Каца технически правильный, он будет очень быстро раздуваться из-за более крупных и сложных анимаций.

Я сделал плагин для таких ситуаций, как ваша, который позволяет использовать функции очередей (с паузой и возобновлением, если это необходимо).

демо: https://jessengatai.github.io/okaynowthis.js/

Решение вашей проблемы с использованием oknowthis.js будет выглядеть так:

$('window').load(function(){

    // keyframe 1
    $('body').okaynowthis('keyframe',0,function(){
        $('div#animateTest1').animate({ left: '+=200' }, 2000);
        // + anything else you want to do

    // keyframe 2 (with 2 seconds delay from keyframe 1)
    }).okaynowthis('keyframe',2000,function(){
        $('div#animateTest2').animate({ left: '+=200' }, 2000);
        // + anything else you want to do

    });

});
person Jesse    schedule 14.10.2016

Вместо создания функции простой способ здесь:

$('#main').animate({ "top": "0px"}, 3000, function()
   {    $('#navigation').animate({ "left": "100px"},  3000, function() 
        {
            alert("Call after first and second animation is completed.");
        })
    }
);

Таким образом, Jquery по умолчанию предоставляет очередь, которая выполняет функцию одну за другой.

person vishal    schedule 26.11.2010
comment
Это не отвечает на вопрос. Вопрос требует, чтобы вы использовали две отдельные функции. Вы не всегда связываете анимацию вместе, иногда можно связать пользовательские функции. - person trusktr; 06.03.2011