Przyszłość responsywnych obrazków z HTML i CSS
[demo url=”http://edu3.home.pl/blog/pliki/picture/” files=”http://edu3.home.pl/blog/pliki/picture/picture.zip”] Jako twórcy stron internetowych, od kilku lat borykamy się z licznymi problemami, których rozwiązywanie jest kluczowe dla poprawnego działania tychże stron…
[demo url=”http://edu3.home.pl/blog/pliki/picture/” files=”http://edu3.home.pl/blog/pliki/picture/picture.zip”]
Jako twórcy stron internetowych, od kilku lat borykamy się z licznymi problemami, których rozwiązywanie jest kluczowe dla poprawnego działania tychże stron na urządzeniach mobilnych. Mowa tutaj o Responsive Web Design i wielu możliwościach, jakie są dla nas dostępne poprzez różne techniki.
Wiemy zatem jak sprawić, by witryna była poprawnie wyświetlana na wielu urządzeniach i różnej wielkości ekranach, jak optymalizować kod, by był on lekki i przyjazny, ale czy poradziliśmy sobie z najważniejszym zagadnieniem – z serwowaniem odpowiednich obrazów dla odpowiednich urządzeń? Na pewno nie poradziliśmy sobie tak, jakbyśmy mogli.
Problemem jest tutaj fakt, iż serwujemy dla urządzeń o małych ekranach duże obrazy i zdjęcia, które dzięki mechanizmom działającym w przeglądarkach internetowych są poprawnie skalowane i wyświetlane. Nie zmienia to jednak faktu, że na urządzeniu o rozdzielczości np. 480px (szerokość) wyświetlamy obraz o szerokości 1024px. Marnujemy zatem przede wszystkim ogromną ilość bajtów przesyłaną przez sieć, co w przypadku urządzeń mobilnych jest niezwykle ważne. Z jednej strony ze względu na szybkość ładowania witryny, a z drugiej na opłaty mobilnego internetu, które często pobierane są za każdy przesłany kilobajt.
Jakie zatem mamy możliwości rozwiązania tego problemu? Otóż – serwerowe i klienckie. Po stronie serwera możemy skorzystać np. z Adaptive Images, gdzie wykryjemy po stronie klienta rozdzielczość urządzenia, a na serwerze przytniemy odpowiednio obrazy z użyciem PHP i zaserwujemy ten najbardziej pasujący.
Idealnie byłoby jednak, gdyby to przeglądarki internetowe udostępniały nam natywny mechanizm, by radzić sobie z serwowaniem odpowiednich obrazków dla użytkownika końcowego. Innymi słowy, chcemy, aby przeglądarka wykonała za nas „brudną” robotę.
No dobrze, czy jest zatem taka możliwość?
I tak i nie. Tak, dlatego że trwają prace nad stworzeniem takiego standardu i część specyfikacji została już zaimplementowana w najnowszych przeglądarkach, a nie, właśnie dlatego, że wsparcie jest póki co bardzo słabe (o tym później).
Nie oznacza to jednak, że z przedstawionych za moment technik nie możemy już teraz korzystać. Otóż możemy, bo dostępne są już skrypty, które pozwalają brakujące implementacje uzupełnić.
Spójrzmy zatem nad czym pracuje grupa z W3C i w czym pokładamy nadzieje, na ostateczne rozwiązanie problemu serwowania obrazów w RWD.
atrybut srcset
Na początek zerknijmy na sposób działania nowego atrybutu srcset
, który przypiszemy do elementu img
w taki sposób:
<img src="obrazek.jpg" srcset="obrazek.jpg, obrazek2x.jpg 2x" alt="Obrazek">
Jak widzisz, do elementu img
przypisaliśmy również znamy nam obecnie atrybut src
, aby przeglądarki, które nie wspierają srcset
mogły nadal poprawnie wyświetlać obrazek. Interesująco robi się natomiast nieco dalej. Zauważ, że w atrybucie srcset
podaliśmy ścieżki do dwóch obrazków, oddzielone przecinkiem, a po drugiej ścieżce i spacji również 2x. Co to oznacza?
Wyobraź sobie, że korzystamy z ekranów o wysokiej rozdzielczości, a dokładnie gęstości pikseli, np. ekranu Retina w Macbooku. Z uwagi na dużą gęstość pikseli, normalne obrazki nie wyglądają tam oszałamiająco. Możemy zatem zaserwować dla takiego ekranu alternatywny, większy obrazek, który zostanie zeskalowany w dół tak, aby pasował do layoutu, ale równocześnie był bardzo ostry. Jeśli obrazek.jpg miał szerokość np. 1280px, to obrazek2x.jpg zapiszemy w szerokości 2560px. Zapis 2x informuje przeglądarkę, że obrazek ten jest przeznaczony dla wyświetlaczy o dużej gęstości pikseli, czyli min-device-pixel-ratio: 2
.
W CSS, moglibyśmy to załatwić np. w taki sposób:
.icon { background-image: url(icon.png); } @media(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { .icon { background-image: url(icon2x.png); background-size: 100%; } }
natomiast nie mieliśmy dotąd możliwości załatwienia tego dla elementów img
bez dodatkowych skryptów.
Oprócz 2x możemy tworzyć zapisy typu 4x, 6x itd.
Zamiast takiego zapisu, możemy użyć również innego, który połączymy za moment z atrybutem sizes
. Zapis ten to szerokoscw, np.:
<img src="obrazek.jpg" srcset="obrazek.jpg 1024w, obrazek2x.jpg 2048w" alt="Obrazek">
Dzięki temu dajemy przeglądarce coś, czego sama nie może sprawdzić na samym początku renderowania strony, mianowicie szerokość poszczególnych obrazków. My, tworząc witrynę znamy ich szerokość. Spójrzmy zatem, co dzięki temu zyskujemy.
atrybut sizes
Powyższy zapis służy nam w połączeniu z atrybutem sizes
, który zapisujemy wg następującego schematu:
sizes="[(media query)] szerokosc_obrazka, [(media query)] szerokosc_obrazka, ..."
Łączymy zatem w pary znane nam z CSS3 media queries, a także przewidywaną szerokość, jaką zajmie obrazek.
Wyobraźmy sobie sytuację, że mamy layout 4 kolumnowy. Każda kolumna zawiera wypis artykułu i obrazek z miniaturką. Każdy o szerokości 200px. Zatem na ekranach Retina będą nam potrzebne obrazki o szerokości 400px. Pomyślmy jednak o urządzeniach przenośnych, gdzie ekran jest mniejszy. W CSS ustaliliśmy sobie breakpoint na 480px (@media(min-width: 480px)
), gdzie chcemy z layoutu 4 kolumnowego, zrobić jednokolumnowy. Teraz wypadałoby, aby nasze obrazki miały szerokość powiedzmy (po odjęciu paddingów) 440px.
Nie ma problemu! Powiedzmy o naszych planach przeglądarce, a ta wczyta odpowiedni obrazek, dbając także o jego odpowiednią rozdzielczość (np. 2x):
<img src="obrazek.jpg" sizes="(min-width: 480px) .25vw, 100vw" srcset="miniatura_normal.jpg 600w, miniatura_small.jpg 300w" alt="Obrazek">
Zauważ, że mówimy tutaj „jeśli jest spełniony warunek, że szerokość okna ma minimum 480px, to obrazki przyjmą mniej więcej 1/4 szerokości okna (bo mamy 4 kolumny). W przeciwnym wypadku (i tutaj już nie musimy podawać media query, będą zajmować 100% okna (vw = viewport). I ważne jest tutaj, aby uświadomić sobie, że mówimy o szerokości okna, a nie kontenera, w którym będą znajdować się obrazki. Jeśli pierwszy warunek będzie spełniony, to przeglądarka sprawdzi szerokość okna (np. 1200px), podzieli to sobie przez 4 (bo użyliśmy zapisu .25vw) i z tego wywnioskuje, że wystarczy wczytać obrazki o szerokości 300px. Nie jest to jednak pewne, gdyż algorytm ten jest bardziej skomplikowany. Na nasze szczęście, przeglądarka zrobi to najmądrzej, jak dana sytuacja to umożliwia. Co ważne, mobilne przeglądarki wezmą też pod uwagę szybkość przesyłu danych. Zatem na desktopie, być może zostanie wczytany obrazek o szerokości 600px, a na urządzeniu mobilnym 300px.
Uwaga! Podawane w atrybucie sizes
media queries powinny odpowiadać tym wpisanym w CSS, dzięki którym dostosowujemy layout do różnych szerokości.
Do tej pory mówiliśmy o sytuacji, kiedy chcemy zaserwować takie same obrazki, lecz w różnych rozmiarach i rozdzielczościach. To jednak nie wszystko, gdyż dla urządzeń mobilnych możemy przecież serwować inne obrazki (np. inaczej przycięte). Na to pozwalać nam będzie element picture
.
Nowy element picture
Nie zawsze zeskalowany obrazek, wyświetlony na małym ekranie, odpowiada temu, co chcieliśmy przekazać. Warto zatem zaserwować alternatywne obrazki dla różnych rozdzielczości. Sprawdźmy jak zrobić to z użyciem nowego elementu `picture` na praktycznym przykładzie:
<picture> <source media="(min-width: 1024px)" srcset="images/clock.jpg 1x, images/clock2x.jpg 2x"></source> <source media="(min-width: 640px)" srcset="images/clock640.jpg, images/clock640_2x.jpg 2x"></source> <source media="(min-width: 480px)" srcset="images/clock480.jpg, images/clock480_2x.jpg 2x"></source> <source media="(min-width: 320px)" srcset="images/clock320.jpg, images/clock320_2x.jpg 2x"></source> <img src="images/clock.jpg" alt=""> </picture>
Jak widzisz, wewnątrz elementu picture
zawarliśmy szereg elementów source
. Na samy końcu musimy również dodać element img
, który będzie odpowiedzialny za wyświetlenie obrazka. W przeglądarkach, które nie wspierają elementu picture
, zostanie po prostu wyświetlony standardowy img
. W pozostałych, przeglądarka będzie musiała podjąć decyzję, który obrazek wybrać i wyświetlić we wspomnianym elemencie img
.
Różne źródła podajemy w elementach source
. Atrybut srcset
już znasz i tutaj nie ma żadnych różnic. Jeśli chodzi o atrybut media
, to tutaj podajemy również media query, od którego będzie uzależniony wybór odpowiedniego obrazka. Powyższy przykład, możesz zobaczyć w DEMO, które przygotowaliśmy (zmniejszaj powoli okno przeglądarki).
Kiedy używać srcset dla img, a kiedy picture?
Obie te techniki nieco się od siebie różnią. W przypadku picture
, przeglądarka musi wczytać ten obrazek, który przypisany jest do danego media query. W przypadku sizes
i atrybutu srcset
dla elementu img
jest tak, jak wspominałem, a zatem przeglądarka bierze pod uwagę jeszcze wiele innych czynników i dopiero wówczas wybiera odpowiedni obraz.
Drugą sprawą jest kwestia, czy serwujemy obrazki wyłącznie w różnych rozdzielczościach, czy może zaprezentowane w inny sposób? Specyfikacja radzi, aby używać elementu picture
tam, gdzie chcemy zaserwować różnej wielkości obrazki, ale także np. inaczej przycięte lub prezentujące ten sam przedmiot, ale w nieco inny sposób. W sposób, który będzie lepiej wyglądał np. na ekranie telefonu komórkowego.
Jakie jest wsparcie dla opisywanych technik i czy powinienem z nich korzystać?
Wsparcie jest na chwilę obecną bardzo słabe. Jeśli chodzi o sam atrybut srcset
, to jest on wspierany w Google Chrome, Safari i Operze, natomiast wkrótce ma się pojawić również w Firefox Nightly i w niedalekiej przyszłości zapewne w standardowej wersji Firefoxa.
Element picture
nie jest natomiast wspierany póki co przez żadną przeglądarkę. Nie zmienia to jednak faktu, że możemy z wszystkiego, co wyżej opisałem zacząć korzystać już dziś. Czy polecam to robić? To zależy od sytuacji. Warto natomiast znać nowe możliwości, by zacząć z nich korzystać jak najszybciej.
Ok, spróbuję skorzystać. Od czego zacząć?
Jak wspomniałem, braki w implementacji, jak zazwyczaj, możemy nadrobić stworzonymi przez społeczność rozwiązaniami. Nie inaczej jest w tym przypadku.
Aby poprawnie obsłużyć opisane tutaj techniki, polecam skorzystanie ze skryptu Picturefill. Wystarczy, że dodamy do naszej strony owy skrypt i nic więcej nie będzie nas interesować. Warto również stworzyć w locie element picture
, aby przeglądarka pozwalała na przypisywanie mu CSS:
document.createElement("picture");
Tyle wystarczy!
Zauważ, jak teraz zachowuje się przeglądarka po wczytaniu strony przy pełnej szerokości okna: (Chrome, wspiera srcset
, nie wspiera picture
):
a także przy zeskalowanym do rozdzielczości mobilnej oknie:
Jak widzisz, w kolumnie Initiator w przypadku forrest2x.jpg widzimy Parser – bo przeglądrka wspiera srcset
i wybrała odpowiedni obraz 2x (korzystam z wyświetlacza Retina), natomiast w przypadku clock2x.jpg oraz clock480_2x.jpg widzimy nazwę skryptu, a także dopisek Script. Oznacza to, że pobieranie tych plików zainicjował załadowany przez nas skrypt, a więc polyfill działa jak należy.
Podsumowanie
Cieszy fakt, że Responsive Images Community Group pracuje nad stworzeniem standardów (link do specyfikacji), które pozwolą nam rozwiązać problem serwowania odpowiednich obrazów, dla odpowiednich urządzeń. Z naszej strony to dodatkowa praca, by przygotować stosowne obrazki, ale z drugiej strony od czego mamy programowanie serwerowe?
Artykuł ten powstał po to, byś już teraz mógł zaznajomić się z nowościami, a w przyszłości zaczął z nich bezboleśnie korzystać. Polecam stworzenie własnych przykładów i być może użycie nowych technik w którymś w kolejnych projektów (?).