Управляємо завантаженням зображень

1

Від автора: Схоже, що завантаження зображень або повністю упускається з виду, або доручається надмірно великим плагінів. Красива, гладка і швидке завантаження сайту – найважливіша частина гарного інтерфейсу враження, а також – елементарна ввічливість по відношенню до вашого дизайнера. Кому хочеться кожен раз при вході на сторінку бачити свій дизайн зіпсованим повільної порядкової завантаженням зображень?

Безліч сайтів, над якими я працюю, сильно перевантажені фотографіями, і всі переваги швидкісного Інтернету зводяться на немає необхідністю надання матеріалів в ультрависокому вирішенні retina для все зростаючого кількості пристроїв. Це – самий підходящий момент, щоб взяти кермо влади в свої руки і почати контролювати завантаження зображень, і в цій статті я покажу вам чотири легких методу, за допомогою яких ваш сайт буде чудово виглядати, і здорово збільшить свою продуктивність.

Управляємо завантаженням зображень

Кожен раз при вигляді повільно завантаження зображення я згадую цю класичну сцену з хлопцем з коміксу.

1. Завантаження кожного зображення окремо (Single-Asset)

Це методика, яку можна застосувати до будь-якого або всіх зображеннях свого сайту для запобігання (або хоча б приховати) традиційної порядкового завантаження базових JPG’ів. Ми почнемо з упаковки кожного зображення div з класом img_wrapper:

Управляємо завантаженням зображень

Цей пакувальник надасть нам деяке додаткове керування розмірами наших зображень і їх коефіцієнтом пропорційності, чого не може гарантувати простий тег img. Він також дасть можливість застосувати спиннер завантаження як фонове зображення, або як окремий елемент, який після закінчення завантаження зображення можна приховати.

Для цього прикладу обмежимо своє зображення коефіцієнтом пропорційності 4:3 – дуже важливо для адаптивних сайтів. Зверніть увагу, що ми також приховали зображення з допомогою opacity: 0;, що дає нам можливість керувати тим, як і коли ми його побачимо при настанні потрібного моменту.

.img_wrapper{
position: relative;
padding-top: 75%;
overflow: hidden;
}
.img_wrapper img{
position: absolute;
top: 0;
width: 100%;
opacity: 0;
}

Кожна картинка в DOM запускає подія load, коли всі його дані завантажені з сервера, а саме зображення відображається браузером. Щоб захопити і прив’язати це подія, нам знадобиться використовувати JavaScript. Я почну з додавання атрибута onload до тегу зображення.

Управляємо завантаженням зображень

До відома новачків, ще не бачили нічого подібного – це називається вбудованим атрибутом скрипта, і дозволяє нам прив’язати функціональність JavaScriptа прямо до подій, що запускається з елементів DOM, що багато в чому схоже з додаванням стилів безпосередньо до елементів за допомогою вбудованого атрибута style. Вірте чи ні, але на початку розвитку Веба ці вбудовані атрибути скрипта займали значну частину написання JavaScript’а і, подібно вбудованим стилям, засуджуються в наші дні борцями за чистоту коду і семантику.

Тому ті з вас, хто відчуває огиду при вигляді вбудованого JavaScriptа і вже готовий пуститися навтьоки, будь ласка, затримайтеся і повірте мені на слово, що це все ще єдиний самий ефективний і надійний метод захоплення події load зображення в DOM. Хоча я цілком і повністю підтримую прогрес і HTML5 – я не маю абсолютно нічого проти застосування прийомів старої школи, якщо вони елегантні та функціональні.

Альтернативою цьому є індивідуальна прив’язка події load до кожного зображення у document ready. Однак проблема виникає, коли зображення завантажуються перш запуску document ready, і до того, як у нас з’являється можливість прив’язати функціональність до події завантаження кожного зображення. Окрема проблема, коли зображення вже кешованими браузером з попередньої сесії, і завантажуються миттєво. Ми пропускаємо подія, а наша функція не викликається. У атрибута onload відсутні такі проблеми, тому що він, так сказати «попередньо прив’язаний» до події і тому обробляється, коли браузер аналізує HTML.

Я абсолютно не маю нічого проти використання прийомів старої школи, якщо вони елегантні та функціональні.

При доданому атрибуті onload в момент завантаження зображення буде викликатися функція imgLoaded(). Її потрібно помістити в файл javascript в head сторінки (після jQuery, якщо ви його застосовуєте у своїй функції, і після будь-яких інших плагінів) з тим, щоб вона компилировалась до парсинга body і завантаження зображень. Якщо вставити функцію внизу сторінки, висока ймовірність того, що зображення стануть грузиться до визначення функції.

З допомогою ключового слова this ми можемо послати необроблений об’єкт DOM зображення в свою функцію JavaScript в якості аргументу:

function imgLoaded(img){
var $img = $(img);
$img.parent().addClass(‘loaded’);
};

Або простим JavaScript’ом:

function imgLoaded(img){
var imgWrapper = img.parentNode;
imgWrapper.className += imgWrapper.className ? ‘loaded’ : ‘loaded’;
};

З допомогою javascriptа можна швидко пройти DOM на один рівень вгору і додати до блогу елементу пакувальника клас loaded. Я впевнений, ви погодитеся з тим, що це дивно елегантне рішення. Вибірково призначивши цього класу стилі, можна тепер показати свою завантажене зображення, встановивши його непрозорість на 1:

.img_wrapper.loaded img{
opacity: 1;
}

Щоб процес відбувався гладко, ми додамо до img кілька переходів CSS3 і доб’ємося при завантаженні свого зображення ефекту «поступового прояви».

.img_wrapper img{
position: absolute;
top: 0;
width: 100%;
opacity: 0;
-webkit-transition: opacity 150ms;
-moz-transition: opacity 150ms;
-ms-transition: opacity 150ms;
transition: opacity 150ms;
}

Дивіться працюючий приклад на codepen.io, що включає альтернативну версію з відображенням спиннер завантаження.

CODEPEN.IO DEMO

Прогресивні JPGи

В якості примітки до цієї техніки і у відповідь на деякі отримані мною до цієї статті відгуки, безперечно варто згадати «прогресивні» JPG’в. Це ще одна давня методика родом з 1990-х, зачіпає збереження JPG’ів як «прогресивних», а не «звичайних» з метою запобігання порядкового завантаження – а замість неї покадрово прорисовуючи зображення однієї висоти по мірі його завантаження. Основна перевага цього прийому полягає в тому, що він запобігає «стрибки» прибуває контенту на сторінці при завантаженні зображень.

Дратують такі ефекти, як спінер завантаження і поступовий прояв – це справа особистого смаку, але в основному прийом з упаковывающим div ом вирішує ці проблеми з допомогою мінімуму CSS і JavaScript’а.

У застосуванні техніки упаковывающего div а найкраще те, що можна не хвилюватися з приводу зміни висоти зображень по мірі їх завантаження, а також не слід прирікати своїх користувачів на потворне об’єднання пікселів в групи, що, на мою думку, може для користувача виявитися настільки ж дратівливим, як і звичайна завантаження. До того ж вона нічого не коштує – процес повторного відображення малюнка по кілька разів насправді створює додаткове навантаження на малопотужні мобільні пристрої. Дратують такі ефекти, як спінер завантаження і поступовий прояв – це справа особистого смаку, але в основному прийом з упаковывающим div ом вирішує ці проблеми з допомогою мінімуму CSS і JavaScript’а, і відсутня необхідність покладатися на користувача (у ситуації з CMS) при збереженні JPG’ів певним способом.

2. Пакетна завантаження декількох зображень

Вищеописана техніка дуже хороша для окремих зображень, але як бути, якщо у нас є підбірка зображень, які потрібно відобразити в «каруселі» або слайдшоу, або якщо ми користуємося плагіном розмітки начебто Masonry? Звичайна помилка при використанні плагінів «каруселі»/слайдера – їх обробка при document ready, найчастіше до завантаження всіх їх зображень. Це може викликати перехід слайдшоу до порожнього, ще не загрузившемуся зображення, особливо якщо ми маємо справу з фотографіями з високою роздільною здатністю і великим розміром файлу.

Для попередження цього явища потрібно піддавати обробці бажаний вами плагін тільки тоді, коли всі необхідні зображення вже завантажилися. Застосувавши варіацію вищенаведеної техніки, ми знову додамо атрибут onload до всіх фотографій свого слайдшоу:

NB: Нижчеприведена розмітка наводиться тільки як спрощений приблизний варіант розмітки плагіна слайдшоу, і повинна бути адаптована у відповідності з вашими вимогами.

Управляємо завантаженням зображень
Управляємо завантаженням зображень
Управляємо завантаженням зображень

У JavaScript’е ми застосуємо функцію slideLoaded() для відстеження процесу завантаження зображень, і піддамо свій плагін обробці, коли готовий:

function slideLoaded(img){
var $img = $(img),
$slideWrapper = $img.parent(),
total = $slideWrapper.find(‘img’).length,
percentLoaded = null;
$img.addClass(‘loaded’);
var loaded = $slideWrapper.find(‘.loaded’).length;
if(loaded == total){
percentLoaded = 100;
// ІНІЦІАЛІЗУВАТИ ПЛАГІН
$slideWrapper.easyFader();
} else {
// ВІДСТЕЖТЕ ПРОЦЕС
percentLoaded = loaded/total * 100;
};
};

Кожен раз при завантаженні матеріалу ми додаємо до нього клас loaded для стеження за його прогресом.
З допомогою останнього if ми ініціалізуємо плагін (в даному випадку jQuery EasyFader), коли кількість зображень з класом loaded дорівнює загальній кількості зображень у контейнері. В якості додаткового властивості можна розділити змінну loaded на змінну total і використовувати її для візуалізації прогресу для користувачів: або шляхом відображення відсоткового співвідношення, або використовуючи її для управління шириною індикатора виконання, або іншим подібним чином.

І знову цей скрипт повинен бути поміщений в head вашого документа, після jQuery і того плагіна, який ви будете піддавати обробці, коли готовий.

3. Попереднє кешування зображень для швидкодії

На сайтах з великою кількістю зображень можна трохи знизити навантаження при завантаженні зображень шляхом фонової завантаження зображень в кеш браузера, поки користувач бродить по сторінці.

Наприклад, скажімо, у мене є багатосторінковий сайт, де у кожної другорядної сторінки є угорі зображення у високому дозволі. Замість того, щоб змушувати користувача чекати завантаження всіх цих зображень кожен раз при переході на окрему сторінку, їх можна завантажити в кеш до того, як користувач потрапить на сторінку. Давайте почнемо з поміщати їх ДО и в масив:

var heroArray = [
‘/uploads/hero_about.jpg’,
‘/uploads/hero_history.jpg’,
‘/uploads/hero_contact.jpg’,
‘/uploads/hero_services.jpg’
]

Зазвичай я б скористався своєю CMS або будь-яким іншим бекендом, на чому створюю сайт, для передачі цих даних на саму сторінку у формі тега script в нижньому колонтитулі. Таким чином, список зображень можна динамічно оновлювати і розширювати.

Коли користувач зайде на домашню сторінки мого сайту, я дочекаюся її повного завантаження, перед тим, як щось робити, щоб переконатися, що я не перериваю завантаження вмісту самої сторінки, додаючи сторонюю завантаження. Для цього, я прикреплю функціональність JavaScript’а до події window load, запускающемуся тільки коли весь вміст сторінки вже завантажений і відображено (включаючи зображення), на відміну від події document ready, яке запускається, як тільки готовий DOM:

function preCacheHeros(){
$.each(heroArray, function(){
var img = new Image();
img.src = this;
});
};
$(window).load(function(){
preCacheHeros();
});

Або простим JavaScript’ом:

function preCacheHeros(){
for(i = 0; i < heroArray.length; i++){
var url = heroArray,
img = new Image();
img.src = url;
};
};
window.onload = preCacheHeros();

З допомогою циклу ми проходимо по масиву heroArray, створюючи порожній об’єкт зображення на кожній ітерації, а потім задаємо URL для атрибуту src нашого зображення. Таким чином зображення завантажується в кеш браузера для поточної сесії з тим, щоб, коли користувач дійсно відвідав сторінку, на якій представлено зображення, воно тут же миттєво відобразилося.

На жаль, хоча практика попереднього кешування прискорює завантаження і покращує враження користувача на стороні клієнта, насправді вона збільшує навантаження на сервер. В такому випадку має сенс перед здійсненням попереднього кешування розглянути аналітику вашого сайту. Якщо більша частина ваших користувачів заходить на головну сторінку вашого сайту і залишають її до відвідування другорядних сторінок, вартість додаткових запитів до вашого сервера може переважити переваги, що подаються тим кільком користувачам, які залишаються.

Має сенс розглянути аналітику вашого сайту до впровадження попереднього кешування

Прекогнитивное попереднє кешування

Управляємо завантаженням зображень

Разом з тим, якщо ви дійсно хочете застосувати попереднє кешування, я раджу розділити зображення для нього значущі групи, стратегічно розміщені на сайті. Наприклад, знаючи про те, що переважна більшість залишаються на сайті користувачів після відвідування домашньої сторінки перейдуть до другорядної, я б попередньо кэшировал основне зображення другорядної сторінки, поки вони знаходяться на домашній сторінці.

Давайте, однак, уявімо, що у кожного з постів мого блогу також є зображення у високому дозволі. Їх попереднє кешування, поки відвідувач знаходиться на головній сторінці, не тільки завдасть сильного удару по серверній стороні, але й може виявитися марною витратою ресурсів, так як, скажімо, лише 15% заходять на домашню сторінку користувачів переходять до посту блогу. Найкращим місцем для попереднього кешування зображень блогу може виявитися цільова сторінка, якщо знати, що майже всі користувачі перейдуть звідти до посту блогу.

Це відомо як прекогнитивное попереднє кешування, де ми використовуємо статистичні дані своєї аналітики для передбачення поведінкових шаблонів переміщення наших користувачів сайту. Можливо, звучить трохи фантастично, але ви здивуєтеся тому, наскільки точно можна передбачити потік відвідувачів і перетворити його в своє (і користувачів) перевага.

Можливо, звучить трохи фантастично, але ви здивуєтеся тому, наскільки точно можна передбачити потік відвідувачів

4. «Ледача» завантаження зображення для зниження навантаження на сервер

Поняття lazy-loading відноситься до зображень, які завантажуються програмним шляхом після спеціального події, щоб заборонити браузеру запитувати і відображати всі зображення на сторінці як тільки документ завантажується.

В основному застосовується В довгих або перевантажених зображеннями сторінках. На цільовій сторінці блогу Barrel ми поєднуємо «ліниву» завантаження з плагіном MixItUp для того, щоб гарантувати, що зображення, відсутні в поточному фільтрі або сторінці, не будуть завантажуватися без особливої на те потреби, поки цей елемент не стане видимим. Будь-які зображення, які підлягають «ледачою» завантаженні, ми знову обернем у div з класом img_wrapper, якому в свою чергу задамо клас lazy_load для того, щоб можна було легко їх вибрати з допомогою jQuery:

Управляємо завантаженням зображень

Зверніть увагу, що до до зображення звертаються тільки в атрибуті data-src зображення img, а не «src». Це не дає браузеру завантажувати зображення при першій передачі документа. Замість цього ми призначаємо .gif розміром 1 x 1px (сперечаємося, ви вже думали, що побачили останній з них!) атрибуту src у вигляді рядка base64. Перевага цього методу в тому, що він ефективний і запобігає запит до сервера, викликаний зверненням до фізичного файла .gif’а.

Щоб піддати свій зображення «ледачою» завантаженні, потрібно просто взяти значення data-src і призначити його src. З-за цього запуститься подія load, тому нам також знадобиться прив’язати це подія прямо перед призначенням нового src з тим, щоб можна було використовувати перший спосіб для поступового прояви зображення:

function lazyLoad(){
var $images = $(‘.lazy_load’);
$images.each(function(){
var $img = $(this),
src = $img.attr(‘data-src’);
$img
.on(‘load’,imgLoaded($img[0]))
.attr(‘src’,src);
});
};
$(window).load(function(){
lazyLoad();
};

Вищенаведена функція «ліниво» завантажує всі зображення після запуску події window load, але код всередині циклу .each() можна адаптувати так, щоб він підходив до безлічі ситуацій. Дуже поширене його застосування – прикріпити до події window scroll і «ліниво» завантажувати зображення по мірі їх прокрутки вікна перегляду.

Вперед, до завантаження зображень

У процесі експериментування з парою цих методик над безліччю проектів протягом останніх приблизно 12 місяців мені довелося по-справжньому скоротити і відточити їх для застосування в нещодавно переробленому проекті barrelny.com (випущеному назад у світ у квітні), де я застосовував поєднання всіх чотирьох методів для того, щоб забезпечити граціозну завантаження зображень, намагаючись при цьому вичавити кожну унцію продуктивності з екстремально перевантаженого фотографіями та зображеннями вебсайту. Шляхом комбінування попереднього кешування і «ледачою» завантаження з завантаженням сторінок за допомогою AJAX, слайдшоу і посторінкове розбивкою на стороні клієнта, ми змогли створити відмінне враження гладкості і всього сайту.

Намагаючись зробити витяг з цих прийомів для презентації команди розробників Barrel, я був приємно здивований тим, наскільки легковагі всі ці методики на папері – зазвичай 5-10 рядків коду в jQuery – і як легко їх впровадити в будь-який проект. Крім того, каждуя з них можна написати простим JavaScript’ом без зайвих клопотів і додаткового коду, але якщо ви користуєтеся jQuery, як це часто робимо ми, безумовно слід скористатися його перевагами надійних методик проходу DOM.

Ці прийоми, безсумнівно, є єдиним способом виконання їх відповідної функціональності, але їх можна легко адаптувати під існуючі каркаси-фреймворки та плагіни. Якщо ви ще не обмірковували завантаження зображень, сподіваюся, що зробите це зараз! Чому б не застосувати один-два з них в своєму наступному проекті?