Предзагрузка изображений с помощью 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 && 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. Гуглите больше и разнообразней. Это позволяет сократить время на разработку, ведь крайне высока вероятность того, что с вашей задачей уже кто-то сталкивался и она решена.
Спасибо за внимание. И до новых встреч.