ihhaman | 12.07.2013

Предзагрузка изображений с помощью jQuery

Добрый день, друзья.

 

Сегодня я вам расскажу о том, как возникают интересные задачи и какие шаги выполняются на пути их решения.

Пришел однажды наш давний клиент с проблемой: 3D-флеш-галерея на его сайте хороша да красива, но недоступна к просмотру на iOs-устройствах. Подумали и постановили: от флеша отказаться, а анимацию и красоту на JavaScript реализовать. Сказано, сделано. Html-разметка уже была подготовлена, скрипт написан и в разных браузерах отлажен. Дело шло к логическому завершению, но вдруг…

…проявилась неприятная особенность поведения новой галереи: иногда, при сильно загруженном интернет-канале, на доли секунды, вместо красивых изображений мы видели лишь их белые фоны. Спустя мгновения они исчезали, но мельтешение было неприятным. Логическое объяснение подобному эффекту нашлось довольно быстро.

По событию готовности DOM-структуры страницы ($(document).ready) выполнялся скрипт инициализации галереи, который позиционировал слайды нужным образом. И вот слайды уже на месте, а изображения для них загрузиться пока еще не успели. То есть событие готовности разметки наступает раньше полной загрузки картинок. Провели дополнительный эксперимент: на телефоне с ооочень-медленным 3g-интернетом открыли целевую страницу, и наблюдали описанное выше во всей красе.

Оставлять галерею в таком виде не стали, начался поиск способов сделать все «красиво». Путь наметился следующий. При загрузке страницы нужно дождаться момента, когда все изображения действительно подгрузятся, и лишь после этого инициализировать и показать галерею. На время ожидания загрузки показать на экране «крутилку».

Попытка 1. Атака в лоб.

В голове вертится, что событие $(window).load наступает, когда вся страница загружена. А нам это и надо. Меняю пару строчек в коде. Тест! Провален! Хм, как же так…

А вот так, в справке jQuery в описании события load об этом написано: не кроссбраузерно, не всегда и не везде работает. Отличное событие )

Попытка 2. Ajax нам в помощь.

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

Проводим еще один тест и узнаем, что complete в данном случае наступает после отдачи разметки, но не связанных с ней изображений.

Попытка. Решение!

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

Итак, разбираемся, как нужно правильно предзагружать изображения.

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

Пример кода для иллюстрации сказанного:

$(new Image()).attr('src', '/img/preload_me_plz.jpg').load(function() {
    alert('Я загрузилось!');
});

Так мы можем получить извещение о загрузке одного изображения, но у нас-то их много. Значит, процесс должен быть автоматизирован. Например, с помощью такой функции-расширения jQuery (спасибо Александру Музыченко):

$.preloadImages = function () {
	if (typeof arguments[arguments.length - 1] == 'function') {
		var callback = arguments[arguments.length - 1];
	} else {
		var callback = false;
	}

	if (typeof arguments[0] == 'object') {
		var images = arguments[0];
		var n = images.length;
	} else {
		var images = arguments;
		var n = images.length - 1;
	}
	var not_loaded = n;
	for (var i = 0; i < n; i++) {
		jQuery(new Image()).attr('src', images[i]).load(function() {
			if (--not_loaded < 1 &amp;&amp; typeof callback == 'function') {
				callback();
			}
		});
	}
}

Функция preloadImages принимает два параметра:

1. набор ссылок на изображения, которые следует предзагрузить

2. callback-функцию, которая будет вызвана после загрузки всех указанных изображений

Пример вызова:

$.preloadImages(["first_image.jpg","second_image.jpg"], function () {
  alert('Обе картинки загружены.');
});

В рассматриваемом случае с галереей набор ссылок на предзагружаемые изображения  был собран средствами нашей любимой jQuery и передан в функцию таким образом:

var paths  = [];
var images = $('#gallery').find('img');
images.each(function() {
	paths.push($(this).attr('src'));
});

$.preloadImages(paths, function () {
	// инициализация галереи  
});

Выводы и советы:

1. Читать документацию полезно (Помните шутку? Если ничего не помогло, то нужно прочитать инструкцию).

2. Гуглите больше и разнообразней. Это позволяет сократить время на разработку, ведь крайне высока вероятность того, что с вашей задачей уже кто-то сталкивался и она решена.

Спасибо за внимание. И до новых встреч.