Na obsah stránky

Responzivní bitmapové obrázky

Aleš Roubíček | | # permalink

S rozmachem zařízení s dipleji s vysokou hustotou pixelů a nejrůznějších rozměrů nastala potřeba nějak pracovat s bitmapovými obrázky, aby dobře vypadaly a přizpůsobovaly se aktuálnímu layoutu.

Nejrozšířenějším řešením je přístup ignorant, kdy se do stránky vloží obrázek o velikosti 2 MB a pak se pomocí width="100%" nechá krásně responzivně měnit dle rozměrů nadřazeného elementu. Tohle řešení funguje krásně, když stránky testujeme na lokálním disku bez síťových latencí a přísného FUP. Jinak jde o zcela nahovnózní řešení, za které vás nebude mít rád žádný z vašich návštěvníků a vlastně ani ten, kdo bude takový web hostovat.

Správné rozměry

Ještě před pár lety byl tohle celkem slušný oříšek. Jak poslat browseru obrázek, který bude vycházet z aktuálních rozměrů layoutu? Na snadě je použít JavaScript, spočítat potřebné rozměry a na základě výsledků poskládat URL obrázku. Krom toho, že máme nepříjemnou latenci, kdy musíme čekat, až se sestaví DOM a spočítá se layout, abychom mohli spočítat rozměry obrázku, tak dojde nejspíše k layout trashingu, až se obrázek do DOMu vloží. Výsledkem je nepříjemné problikávání a přeskakování.

Žijeme v roce 2016 a většina browserů podporuje media-queries, které nám práci velmi usnadní. Další věcí, která je dnes široce rozšířená, je podpora elementu picture a atributu srcset. Jejich vhodnou kombinací můžeme podchytit většinu případů užití a když ne, tak pořád máme v HTML možnost fallbacku na starý dobrý img.

Správné velikosti obrázků bychom měli odvozovat od breakpointů layoutu. Vyhneme se tím tak potřebě, generovat nekonečnou řadu rozměrů obrázků.

<picture>
    <source media="(max-width: 480px)"
            srcset="phone-1x.jpg,
                    phone-2x.jpg 2x,
                    phone-3x.jpg 3x">
    <source media="(min-width: 481px) and (max-width: 640px)"
            srcset="tablet-1x.jpg,
                    tablet-2x.jpg 2x,
                    tablet-3x.jpg 3x">
    <source media="(min-width: 641px) and (max-width: 840px)"
            srcset="desktop-1x.jpg,
                    desktop-2x.jpg 2x">
    <source media="(min-width: 840px)"
            srcset="fullhd-1x.jpg,
                    fullhd-2x.jpg 2x">
    <img alt="Ukázka" src="fallback.jpg" width="100%">
</picture>

Máme definované zdroje obrázku, které se aktivují, když je splněna podmínka media query v atributu media. Dále dokážeme požádat o různé velikosti obrázku na základě hustoty pixelů na displeji a to v atributu srcset. Musíme ale kvůli jednomu obrázku nařezat jedenáct variant. Ačkoliv se to dá – třeba ve Sketchi – pěkně automatizovat, tak to stejně není moc udržitelné.

Cloudinary

Naštěstí na trhu existuje několik služeb, které usnadňují řezání různých velikostí obrázků. Mně se nejlépe osvědčila služba Cloudinary. Originál – v co nejlepším rozlišení – nahrajeme do této služby a pak už si jen řekneme, co s ním chceme udělat:

https://res.cloudinary.com/*my-account*/image/upload/**dpr_2.0,w_640**/v1449739319/*original.jpg*

Zde jsme přidali transformace pro ořez na šířku 640 px ve verzi pro retina displeje s 2× hustotou pixelů. Popis všech transformací najdete v dokumentaci.

Optimální formáty

Ačkoliv je formát JPEG stále nejrozšířenějším formátem pro obrázky na webu, přece jen je tu s námi od roku 1992. Od té doby se na poli komprese a kódovacích algoritmů přece jen něco událo. Naneštěstí ještě nedošlo k širokému konsensu, který z formátů by ho měl nahradit. A tak máme v Chrome podporu WebP, v Edge zase JPEG XR a v Safari JPEG 2000

Naštěstí Cloudinary umí i doručování v efektivnějších formátech a tak stačí, rozšířit sadu našich zdrojů ještě o nějaké ty formáty:

<picture>
    <!--- Chrome -->
    <source type="image/webp"
            srcset="https://res.cloudinary.com/*my-account*/image/upload/**w_640**/v1449739319/*original.***webp**,
                    https://res.cloudinary.com/*my-account*/image/upload/**dpr_2.0,w_640**/v1449739319/*original.***webp **2x">
    <!-- Edge -->
    <source type="image/vnd.ms-photo"
            srcset="https://res.cloudinary.com/*my-account*/image/upload/**w_640**/v1449739319/original.**jxr**,
                    https://res.cloudinary.com/*my-account*/image/upload/**dpr_2.0,w_640**/v1449739319/original.**jxr** 2x">
    <!-- Safari -->
    <source type="image/jp2"
            srcset="https://res.cloudinary.com/*my-account*/image/upload/**w_640**/v1449739319/*original.***j****p2**,
                    https://res.cloudinary.com/*my-account*/image/upload/**dpr_2.0,w_640**/v1449739319/*original.***j****p2 **2x">
    <!-- Zbytek -->
    <source type="image/jpeg"
            srcset="https://res.cloudinary.com/*my-account*/image/upload/**w_640**/v1449739319/*original.*jpg,
                    https://res.cloudinary.com/*my-account*/image/upload/**dpr_2.0,w_640**/v1449739319/*original.*jpg** **2x">
    <!-- Fallback ... -->
</picture>

Teď si uděláme kartézský součin s potřebnými media queries a vidíme, kolik práce nám Cloudinary ušetří s řezáním obrázků! Bohužel komplexita kódu, který se nám stará o natažení správného obrázku, nám krapet prolítla střechou. To se samozřejmě dá řešit nějakou komponentou, která nám kód hezky vygeneruje, ale o tom třeba někdy příště. Každopádně bacha, aby jen vložení obrázku nebylo větší než obrázek samotný! :)

Našli jste v článku chybu? Máte námět na reportáž? Založte mi ticket.