Главная страница   /   10.5. Создание и управление очередью эффектов (Pro jQuery

Pro jQuery

Pro jQuery

Адам Фриман

10.5. Создание и управление очередью эффектов

Когда вы используете эффекты, jQuery создает очередь анимаций, которые он должен выполнить друг за другом. Есть набор методов, которые можно использовать, чтобы получить информацию об очереди эффектов или управлять ей. Эти методы описаны в таблице 10-7.

Таблица 10-7: Методы для работы с очередью эффектов
Метод Описание
queue() Возвращает очередь эффектов, которая должна быть выполнена для элементов в объекте jQuery
queue(function) Добавляет функцию в конец очереди
dequeue() Удаляет и выполняет первый пункт в очереди для элементов в объекте jQuery
stop()
stop(clear)
stop(clear, jumpToEnd)
Останавливает текущую анимацию
delay(time) Вставляет задержку выполнения между эффектами в очереди

Очередь эффектов можно создать, если объединить цепочкой вызовы методов, работающих с эффектами, как показано в листинге 10-15.

Листинг 10-15: Создание очереди эффектов
<script type="text/javascript">
	$(document).ready(function () {
		$('form').css({ "position": "fixed", "top": "70px", "z-index": "2" });
		$('h1').css({ "position": "fixed", "z-index": "1", "min-width": "0" });
		var timespan = "slow";
		cycleEffects();
		function cycleEffects() {
			$('h1')
				.animate({ left: "+=100" }, timespan)
				.animate({ left: "-=100" }, timespan)
				.animate({ height: 223, width: 700 }, timespan)
				.animate({ height: 30, width: 500 }, timespan)
				.slideUp(timespan)
				.slideDown(timespan, cycleEffects);
		}
	});
</script>

Скрипт в этом примере использует обычную цепочку методов jQuery, чтобы объединить серию эффектов для элемента h1. Последний эффект в качестве функции обратного вызова использует функцию cycleEffects, которая заново запускает анимацию. Сначала это завораживает, потом начинает немного раздражать, а потом эта анимация попадает в категорию тех, которые просто начинают бесить. Но все это действо создает очередь эффектов, о чем я и должен был рассказать в этом разделе.

Примечание

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

Отображение элементов в очереди эффектов

Можно использовать метод queue, чтобы проверить содержание очереди эффектов. Это не совсем полезно, потому что очередь содержит объект одного из двух типов данных. Если выполняется эффект, тогда соответствующее звено в очереди – это строковое значение inprogress. Если эффект не выполняется, то звено в очереди – это функция, которая будет вызвана. Все эффекты jQuery выполняются функцией doAnimation, которую вы не можете проверить, чтобы выяснить, для какого пункта будет создана сейчас анимация. Это обозначает, что проверка содержания является хорошей точкой для начала работы с очередью, и в листинге 10-16 показано, как это может быть сделано.

Листинг 10-16: Проверка содержания очереди эффектов
<script type="text/javascript">
	$(document).ready(function () {
		$('h1').css({ "position": "fixed", "z-index": "1", "min-width": "0" });
		$('form').remove();
		$('<table border=1></table>')
			.appendTo('body').css({
				position: "fixed", "z-index": "2",
				"border-collapse": "collapse", top: 100
			});
		var timespan = "slow";
		cycleEffects();
		printQueue();
		function cycleEffects() {
			$('h1')
				.animate({ left: "+=100" }, timespan)
				.animate({ left: "-=100" }, timespan)
				.animate({ height: 223, width: 700 }, timespan)
				.animate({ height: 30, width: 500 }, timespan)
				.slideUp(timespan)
				.slideDown(timespan, cycleEffects);
		}
		function printQueue() {
			var q = $('h1').queue();
			var qtable = $('table');
			qtable.html("<tr><th>Queue Length:</th><td>" + q.length + "</td></tr>");
			for (var i = 0; i < q.length; i++) {
				var baseString = "<tr><th>" + i + ":</th><td>";
				if (q[i] == "inprogress") {
					$('table').append(baseString + "In Progress</td></tr>");
				} else if (q[i].name == "") {
					$('table').append(baseString + q[i] + "</td></tr>");
				} else {
					$('table').append(baseString + q[i].name + "</td></tr>");
				}
			}
			setTimeout(printQueue, 500);
		}
	});
</script>

В этом примере не нужен элемент form, поэтому я удалил его из DOM и заменил его простым table, который я буду использовать, чтобы отобразить содержание очереди эффектов. Я добавил повторяющуюся функцию printQueue, которая вызывает метод queue и отображает в table (таблице) число производимых эффектов и немного информации о каждом этом действии. Как я уже говорил, каждый пункт, представленный в очереди, сам по себе не особенно интересен, но все они дают общее представление о том, что происходит. На рисунке 10-14 показано, как jQuery продвигается по очереди эффектов.

Рисунок 10-14: Проверка содержания очереди

Это пример, который тяжело отобразить в статических рисунках. Я рекомендую вам загрузить документ в браузер и посмотреть самим. Если первой вызывается функция cycleEffects, тогда в очереди будут присутствовать все шесть пунктов, для первого будет показано, что он сейчас выполняется. Другие являются экземплярами функции doAnimation. После того, как выполняется отдельный эффект, jQuery удаляет этот пункт из очереди. В конце выполнения последнего эффекта функция cycleEffects вызывается снова, и это приводит к тому, что в очереди появляются шесть пунктов.

Прерывание эффектов и очистка очереди

Можно использовать метод stop, чтобы прервать выполнение эффекта. Этому методу можно добавить два дополнительных аргумента, оба из которых являются значениями boolean. Если в качестве первого аргумента вы укажете true, тогда все другие эффекты будут удалены из очереди и не выполнены. Если задать true в качестве второго аргумента, тогда CSS свойствам, для которых сейчас создается анимация, будут установлены их целевые значения.

Значением по умолчанию для обоих аргументов является false, что обозначает, что только текущий эффект удаляется из очереди, и что у свойств, для которых создается анимация, остаются значения, которые были установлены в тот момент, когда эффект был прерван. Если вы не очистите очередь, jQuery продвинется к следующему эффекту и будет его выполнять обычным образом. В листинге 10-17 показано использование метода stop.

Совет

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

Листинг 10-17: Использование метода stop
<script type="text/javascript">
		$(document).ready(function () {
			$('h1').css({ "position": "fixed", "z-index": "1", "min-width": "0" });
			$('form').remove();

			$('<table border=1></table>')
				.appendTo('body')
				.css({
					position: "fixed", "z-index": "2",
					"border-collapse": "collapse", top: 100
				});

			$('<button>Stop</button><button>Start</button>')
				.appendTo($('<div/>').appendTo("body")
				.css({
					position: "fixed", "z-index": "2",
					"border-collapse": "collapse", top: 100, left: 200
				}))
				.click(function (e) {
					$(this).text() == "Stop" ? $('h1').stop(true, true) : cycleEffects();
				});

			var timespan = "slow";
			cycleEffects();
			printQueue();

			function cycleEffects() {
				$('h1')
					.animate({ left: "+=100" }, timespan)
					.animate({ left: "-=100" }, timespan)
					.animate({ height: 223, width: 700 }, timespan)
					.animate({ height: 30, width: 500 }, timespan)
					.slideUp(timespan)
					.slideDown(timespan, cycleEffects);
			}

			function printQueue() {
				var q = $('h1').queue();
				var qtable = $('table');
				qtable.html("<tr><th>Queue Length:</th><td>" + q.length + "</td></tr>");
				for (var i = 0; i < q.length; i++) {
					var baseString = "<tr><th>" + i + ":</th><td>";
					if (q[i] == "inprogress") {
						$('table').append(baseString + "In Progress</td></tr>");
					} else if (q[i].name == "") {
						$('table').append(baseString + q[i] + "</td></tr>");
					} else {
						$('table').append(baseString + q[i].name + "</td></tr>");
					}
				}
				setTimeout(printQueue, 500);
			}
		});
	</script>

Чтобы продемонстрировать метод stop, я добавил в документ две кнопки. Когда нажимается кнопка Stop, я вызываю метод stop с двумя аргументами true. В результате этого очищается оставшаяся часть очереди эффектов, а свойство элемента, для которого совершалась анимация, получает целевое значение. Поскольку при использовании метода stop функции обратного вызова не работают, цикл вызовов метода cycleEffects ломается, и анимация останавливается. Когда нажимается кнопка Start, вызывается метод cycleEffects, и анимация возобновляется.

Совет

Если нажать кнопку Start при запущенной анимации, это не создаст путаницу для jQuery. Просто в очередь добавятся эффекты, с которыми работает метод cycleEffects. Использование функций обратного вызова обозначает, что размер очереди немного вырастет, но сама анимация будет дальше нормально выполняться.

Добавить в очередь задержку выполнения

Можно использовать метод delay, чтобы создать паузу между двумя эффектами в очереди. Аргументом этого метода является число в миллисекундах, на которое должна быть установлена задержка выполнения. В листинге 10-18 показано использование этого метода.

Листинг 10-18: Использование метода delay
<script type="text/javascript">
	$(document).ready(function () {
		$('h1').css({ "position": "fixed", "z-index": "1", "min-width": "0" });
		$('form').remove();
		$('<table border=1></table>')
			.appendTo('body')
			.css({
				position: "fixed", "z-index": "2",
				"border-collapse": "collapse", top: 100
			});
		$('<button>Stop</button><button>Start</button>')
		.appendTo($('<div/>').appendTo("body")
		.css({
			position: "fixed", "z-index": "2",
			"border-collapse": "collapse", top: 100, left: 200
		})).click(function (e) {
			$(this).text() == "Stop" ? $('h1').stop(true, true) : cycleEffects();
		});
		var timespan = "slow";
		cycleEffects();
		printQueue();
		function cycleEffects() {
			$('h1')
				.animate({ left: "+=100" }, timespan)
				.animate({ left: "-=100" }, timespan)
				.delay(1000)
				.animate({ height: 223, width: 700 }, timespan)
				.animate({ height: 30, width: 500 }, timespan)
				.delay(1000)
				.slideUp(timespan)
				.slideDown(timespan, cycleEffects);
		}
		function printQueue() {
			var q = $('h1').queue();
			var qtable = $('table');
			qtable.html("<tr><th>Queue Length:</th><td>" + q.length + "</td></tr>");
			for (var i = 0; i < q.length; i++) {
				var baseString = "<tr><th>" + i + ":</th><td>";
				if (q[i] == "inprogress") {
					$('table').append(baseString + "In Progress</td></tr>");
				} else if (q[i].name == "") {
					$('table').append(baseString + q[i] + "</td></tr>");
				} else {
					$('table').append(baseString + q[i].name + "</td></tr>");
				}
			}
			setTimeout(printQueue, 500);
		}
	});
</script>

Этот скрипт вставляет две одно секундные паузы в цепочку анимационных эффектов. Метод delay использует безымянную функцию для обработки задержки выполнения, поэтому в документе вы увидите следующий код в таблице проверки очереди:

function (next, runner) {
	var timeout = setTimeout(next, time);
	runner.stop = function () {
		clearTimeout(timeout);
	};
}

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

Добавление в очередь функций

Можно добавить собственные функции в очередь, используя метод queue, и они будут выполняться как стандартные методы для работы с эффектами. Можно использовать эту возможность, чтобы начать другую анимацию, грациозно выйти из цепочки анимаций, основываясь на внешней переменной, а также делать все, что вам нужно. В листинге 10-19 содержится пример.

Листинг 10-19: Добавление в очередь пользовательской функции
<script type="text/javascript">
	$(document).ready(function () {
		$('form').css({ "position": "fixed", "top": "70px", "z-index": "2" });
		$('h1').css({ "position": "fixed", "z-index": "1", "min-width": "0" });
		var timespan = "slow";
		cycleEffects();
		function cycleEffects() {
			$('h1')
				.animate({ left: "+=100" }, timespan)
				.animate({ left: "-=100" }, timespan)
				.queue(function () {
					$('img').fadeTo(timespan, 0).fadeTo(timespan, 1);
					$(this).dequeue();
				})
				.animate({ height: 223, width: 700 }, timespan)
				.animate({ height: 30, width: 500 }, timespan)
				.slideUp(timespan)
				.slideDown(timespan, cycleEffects);
		}
	});
</script>

Переменная this указывает на объект jQuery, для которого был вызван метод. Это полезно, потому что вы должны убедиться, что вызвали метод dequeue в вашей функции, чтобы переместить очередь к следующему эффекту или функции. В этом примере я использовал метод queue, чтобы добавить функцию, которая делает элементы img полностью прозрачными и обратно.

Совет

Эффекты, которые я добавил в пользовательскую функцию, присоединены к очереди эффектов для элементов img. У каждого элемента своя собственная очередь, и вы можете управлять ими независимо друг от друга. Если вы хотите создать анимацию для множества свойств тех же элементов, с чьими очередями вы работаете, вы просто используете метод animate. Иначе ваши эффекты просто будут добавлены друг за другом в очередь.

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

Листинг 10-20: Использование аргумента, переданного пользовательской функции
<script type="text/javascript">
	$(document).ready(function () {
		$('form').css({ "position": "fixed", "top": "70px", "z-index": "2" });
		$('h1').css({ "position": "fixed", "z-index": "1", "min-width": "0" });
		var timespan = "slow";
		cycleEffects();
		function cycleEffects() {
			$('h1')
				.animate({ left: "+=100" }, timespan)
				.animate({ left: "-=100" }, timespan)
				.queue(function (nextFunction) {
					$('img').fadeTo(timespan, 0).fadeTo(timespan, 1);
					nextFunction();
				})
				.animate({ height: 223, width: 700 }, timespan)
				.animate({ height: 30, width: 500 }, timespan)
				.slideUp(timespan)
				.slideDown(timespan, cycleEffects);
		}
	});
</script>

Совет

Если вы не вызовете следующую функцию или метод dequeue, тогда последовательность выполнения эффектов остановится.