Dynamicky zvětšované sprite

Technika spritů je nejjednodušší způsob, jak zrychlit načítání stránky, pokud na ní potřebujete mít velké množství ikonek nebo jiných (zpravidla relativně malých) obrázků.

Problém ale začíná s tím, jak sprite správně vytvořit a nastavit, aby se zobrazila požadovaná ikona (a nebyly vidět ty sousední). Nejobtížnější pak bývá udělat ikony responzivní, aby se například vždy přizpůsobili na výšku řádky v menu.

Omezení prohlížečů při načítání spočívá v tom, že mohou stahovat současně pouze omezené množství zdrojů (JS, CSS, obrázků, apod.). Zpravidla jde o 4 requesty současně (i když jde u většiny prohlížečů tuto hodnotu změnit v nastavení, její zvýšení může naopak zpomalit nebo dokonce zablokovat připojení k internetu). Další problém je trvání každého requestu, který se skládá z několika kroků (překlad adresy v DNS, navázání spojení, odeslání požadavku, čekání na vyřízení, atd.) a i když se řada z nich řeší pomocí cache (DNS), keep-alive (navázání spojení), atd. tak i když stihnete 1 request za 20ms, tak při 100 requestech bude jen vyřízení requestů trvat 2 sekundy (ve kterých není započten vlastní přenos dat!!!).

Trik spočívá v tom, že všechny obrázky spojíte do jednoho velkého, na jehož stažení pak prohlížeči stačí jediný request. Správnou ikonu pak ze spritu zobrazíte pomocí css vlastnosti background-position.

1. Rozměry

První krok, který je potřeba si ujasnit, je jaké rozměry budou ikony mít. Pro příklad řekněme, že základní velikost ikony bude 32px (čtverec) – to je pro desktop. Pro mobilní zařízení pak budete muset zajistit ikony 48px (Android), 64px (iPhone) a 96px (iPhone 6+, Samsuns S6, apod.). Pro zařízení s nízkým DPI (např. chytré televize) se může ještě hodit rozměr 16px.

Ikony vždy připravujte tak, že navrhnete tu největší, a pak ji budete postupně zmenšovat – a případně zjednodušovat tak, aby byla stále přehledná. Například, pokud má být pro login ikona uživatele s visacím zámkem, pro rozměr 96px může mít uživatel obličej (oči a ústa), zatímco do ikony 16px s obtížemi nacpete jen samotný zámek bez uživatele aby bylo stále poznat, že je to zámek.

Následně si rozvrhněte, jak chcete ikony do spritu rozmístit. Nejlepší je ikony umisťovat pod sebe nebo vedle sebe do jedné řady – vždy v opačném směru než plánujete jejich umístění (např. pro vodorovný toolbar umisťovat ikony pod sebe, pro svislé menu naopak vedle sebe).

Dlouhé úzké obrázky se ale špatně editují a tak se často místo toho používá dvourozměrný sprite (např. 3 ikony vodorovně, 3 ikony svisle, pokud máte max. 9 ikon). Ten je také dobré používat, pokud mají ikony měnit stav (např. zvýraznění při kliknutí, zešedivění pro vypnutá tlačítka, apod.). Pak se do jednoho rozměru umisťují druhy ikonek (login, search, atd.) a do druhého rozměru pak jejich typy (vypnutá, zvýraznění, atd.)

Lepší je nechávat mezi ikonami prázdné místo odpovídající velikosti ikony. Pokud tedy chcete umístit do spritu 9 ikon (3×3) o rozměrech 32px, měl by výsledný obrázek mít rozměr jako pro 25 ikon (5×5 – 3×3 + 2 místa mezi nimi).

Příklad vidíte v úvodním obrázku článku.

Nedělejte ale příliš velké sprity. Už sprite 6×6 s 36 ikonami o velikost 96×96 pixelů bude mít 1056×1056 pixelů ((6 ikon + 5 mezer) * 96px) a bude zabírat přes půl megabajtu. Na pomalejším připojení tak bude trvat dlouho (několik sekund), než se sprite stáhne a do té doby bude stránka bez ikon. Pokud ikony rozdělíte do více spritů podle toho, kdy se zobrazují, může prohlížeč nejprve stáhnout ikony, které jsou hned vidět (např. v záhlaví a úvodu stránky) a zobrazit je a pak teprve dotáhnout ikony, které se zobrazí až později (např. ikony v patičce, submenu, příp. i hover ikony, apod.).

2. Příprava spritu

Pro editování je dobré použít nějaký program, který umí pracovat s vrstvami. Například PhotoShop, Paint.net, apod.

Začněte tím, že vytvořte jeden obrázek o velikosti ikony (32x32px) a druhý o velikosti spritu (např. 160x160px; ten by měl být ve výchozím stavu zcela průhledný). Do obrázku ikony nakreslete čtverec s černým okrajem a bílou výplní (nebo jakékoliv jiné barvy, které vám přijdou vhodné). Ikonu zkopírujte a vložte do nové vrstvy sprite obrázku na pozici 0x0. Přidejte ikonu ještě dvakrát do dalších vrstev a umístěte je na pozice 0x32 a 32×0. Teď označte ve vrstvě s ikonou na 0x0 plochu o rozměru 64x64px (označena ikonamy v dalších vrstvách), zkopírujte a znovu vložte a posuňte na pozice 0x64 a 64×0 (opět poznáte podle ikon v dalších vrstvách).

Teď můžete smazat vrstvy s ikonami na pozicích 0x32 a 32×0, zkopírovat první vrstvu s ikonami a rozkopírovat je na všechny pozice (vždy ikonou vlevo nahoře překryjte poslední ikonu.

Tím jste si připravili šablonu (síto) pro umisťování ikon (viz úvodní obrázek článku). Vytvořte tedy další vrstvu, do které již budete umisťovat ikony (nebo umístěte každou do samostatné vrstvy – podle možností editoru).

Ikony umisťujte vždy doprostřed vyhrazeného prostoru (32×32). Např. pokud má ikona zámku rozměr 20x28px, měla by být na relativní pozici 6x2px (od začátku ikony) tak, aby měla vlevo a vpravo, resp. nahoře a dole stejné okraje. Nezapomeňte, že většina editorů umožňuje pohybovat vybranou oblastí také pomocí šipek na klávesnici, což je užitečné, pokud se myší nemůžete trefit na požadovanou pozici.

Za žádných okolností nesmí ikona přesáhnou vymezený prostor (32x32px), pokud je větší, musíte ji zmenšit tak, aby se vešla!

Až budou všechny ikony umístěny, doporučuji uložit celý soubor pro pozdější úpravu do formátu, který zachová vrstvy (PSD, PDN, apod.) a následně skrýt vrstvu se šablonou a vyexportovat do PNG.

Stejný postup samozřejmě opakujte pro všechny velikosti ikon (32, 48, 64, atd.). To si můžete zjednodušit tak, že existující sprite zvětšíte nebo zmenšíte a do další vrstvy budete vkládat příslušné ikony přes ty původní (nakonec skryjete vrstvy se šablonou a původními ikonami).

3. CSS pro ikony

Aby se v ikoně ze spritu zobrazila jen příslušná ikona, je potřeba připravit CSS třídy, pomocí kterých budete ikony umisťovat. Každá třída bude definovat na jaké pozici se ikona ve spritu nachází.

Běžně se definují takto:

.icon_login { 
    background-position: -32px -64px; 
}

Tím ale zabráníte tomu, aby mohla ikona reagovat na různé velikosti zdrojového obrázku a rozměry ikony a zcela tak zabijete responzivitu pro mobilní zařízení.

Mnohem lepší je pozici určit procentuálně, čímž bude pozice ikony vždy stejná nezávisle na velikosti obrázku. Pokud např. umístíte ikony do Spritu 3×3, budou ikony na pozicích 0%, 50% a 100%.

Pozice ikon podle počtu ikon ve spritu:

  • 2×2: 0%, 100%
  • 3×3: 0%, 50%, 100%
  • 4×4: 0%, 33%, 66%, 100%
  • 5×5: 0%, 25%, 50%, 75%, 100%
  • 6×6: 0%, 20%, 40%, 60%, 80%, 100%
  • 7×7: 0%, 16%, 33%, 50%, 66%, 83%, 100%
  • 8×8: 0%, 14%, 28%, 42%, 57%, 71%, 85%, 100%
  • 9×9: 0%, 12.5%, 25%, 37.5%, 50%, atd.
  • 11×11: 0%, 10%, 20%, 30%, atd.

Ti matematicky zdatnější si již jistě všimli vzorce, kdy 100% vydělíte číslem o 1 menším než rozměr spritu a získanou hodnotu postupně přičítáte k nule dokud nedojdete do 100%.

Výše uvedené platí i v případě, že budete nechávat mezi ikonami mezery. Např. pro ikony 6×6 bude s mezerami sprite velký 11×11, kde pozice rostou po 10%, ale jelikož každá druhá je prázdná, budou ikony na pozicích 0%, 20%, 40%, atd. stejně jako u 6×6 bez mezer.

Pokud používáte CSS preprocesor, můžete si na to vytvořit funkci (mixin). Příklad pro LESS:

.sprite(@width: 3; @height: 3; @column: 1; @row: 1) {
    background-size: (@width*100%) (@height*100%);
    background-position: 
         (100% / (@width - 1) * (@column - 1))
         (100% / (@height - 1) * (@row - 1))
    ;
}

//zobrazí první sprite ze souboru s 3x3 ikonami
.sprite(3, 3, 1, 1);

Některé rozměry nejsou příliš vhodné – např. rozměr 8×8 je zcela nepoužitelný, protože musíte 100 dělit sedmi, což je 14,2857… Nejvhodnější jsou tedy rozměry o jedna větší než násobky 2 nebo 5, tedy 3, 5, 6, 9, 11, 16, 17, 21, atd. Rozměry, kde dělíte násobky 3 (tedy 4, 7, 10, atd.), se mohou zdát také nevhodné, ale můžete použít funkci calc(), takže pro 4×4 budou pozice 0%, calc(100%/3), calc(200%/3) a 100%:

.icon_4x4 {
    width: 1rem; height: 1rem;
    background-size: 400%;
}
.icon_4x4_1x1 {
    background-position: 0 0;
}
.icon_4x4_2x3 {
    background-position:
         calc(100% / 3) calc(200% / 3);
}
.icon_4x4_4x1 {
    background-position: 100% 0;
}

Pokud vám tedy počet ikon vyjde na nevhodný rozměr, problém můžete jednoduše vyřešit tím, že výsledný sprite zvětšíte do vhodnějšího rozměru, čímž si zároveň usnadníte práci pro případ, že v budoucnu budete potřebovat přidat další ikony (nebudete muset přepočítávat pozice již nastavených ikon). Prázdné (průhledné) místo v obrázku nezabírá příliš místa, takže moc neztratíte. Pokud chcete ušetřit, vyplňte nevyužitá místa černou průhlednou barvou (rgba(0,0,0,0)), která se dá lépe zkomprimovat a tak bude obrázek menší (alternativně můžete použít program zopflipng s parametrem --lossy_transparent, který to provede za vás).

Pamatujte, že procentuální pozice se nepočítá z levého horního rohu, ale rovnoměrně; tedy 0% znamená nalevo/nahoře, 50% je uprostřed spritu a 100% napravo/dole.

Aby procenta fungovala správně, musíte pomocí definice background-size určit správnou velikost pozadí v násobcích rozměru ikony. Nejlépe toho dosáhnete opět pomocí procent, kdy 100% odpovídá šířce ikony. Pro sprite 2×2 bude velikost 200%, pro 4×5 bude rozměr „400% 500%“. Pokud sprite obsahuje pouze jednu řadu nebo sloupec ikon, můžete použít:

background-size: 100%; /* ikony pod sebou */
background-size: auto 100%; /* vedle sebe */

Pokud máte mezi ikonami mezery, nezapomeňte je také započítat; např. sprite s 6×6 ikonami je vlastně velký 11×11 (6 ikon + 5 mezer) a tedy bude velikost 1100%.

Jak už bylo uvedeno, při definování CSS tříd je nejlepší využívat pojmenovaných tříd, kdy každá ikona má nějakou obecnou třídu určující, o jakou ikonu se jedná (např. menu_icon, toolbar_icon) a druhou třídu, která určuje jméno konkrétní ikony (např. icon_login, icon_contact, apod.).

Aby nedošlo k omylu: třída určující typ ikony (menu_icon) se vztahuje ke konkrétnímu spritu a ne k místu, kde se ikona vyskytuje. Pokud např. ikonu pro login potřebujete v toolbaru i menu, buď budete muset umístit ikonu do obou spritů menu.png a toolbar.png a pak použít příslušné třídy („menu_icon icon_login_menu“ a „toolbar_icon icon_login_toolbar„), nebo ji dát jen do jednoho (třeba menu.png) a v druhém případě použít odlišnou třídu:

<a class="toolbar_button_login">
    <span class="menu_icon icon_login">
    Login
</a>

V příkladu je sice umístěna ikona v toolbarovém tlačítku, ale používá třídu menu_icon, protože ikona je umístěna ve spritu pro menu.

Příklad CSS:

.menu_icon {
    background: url(/sprites/menu.png);
    width: 1em; height: 1em;
    background-size: 500%; /* 5x5 ikon */
}
.toolbar_icon {
    background: url(/sprites/toolbar.png)
    width: 32px 32px;
    background-size: 200% auto;
    /* ikony pod sebou, 
          výchozí a hover vedle sebe */
}
.icon_login { /* from menu.png */
    background-position: 0 25%;
    /* druhá ikona v první řadě */
}
.icon_open_menu { /* from toolbar.png */
    background-position: 45% 0;
    /* desátá ikona z jedna dvaceti */
    /* ikona z prvního sloupce */
}
.icon_open_menu:hover {
    background-position: 45% 100%;
    /* ikona z druhého sloupce */
}

Pokud z jakéhokoliv důvodu nemůžete (nechcete) použít konkrétní třídy pro ikony, je vhodnější použít stejné seskupení místo uvádění hodnot pro každou ikonu:

/* špatně */
#menu_contact > a > span {
    width: 1em; height: 1em;
    background: url("/menu.png")
                 25% 75% / 500%;
}
#menu_login > a > span {
    width: 1em; height: 1em;
    background: url("/menu.png")
                 50% 0% / 500%;
}

/* lepší: */
#menu_contact > a > span,
#menu_login > a > span {
    width: 1em; height: 1em;
    background: url(/menu.png);
    background-size: 500%;
}
#menu_contact > a > span {
    background-position: 25% 75%;
}
#menu_login > a > span {
    background-position: 50% 0%;
}
/* teď to není vidět, ale až bude ikon víc,
tenhle zápis bude kratší a přehlednější */

Přidání nových ikon

Jak už bylo zmíněno výše, nevýhodou procentuálního uvádění pozic je to, že v okamžiku, kdy do spritu přidáte další ikony a sprite tak zvětšíte, musíte přepočítat pozice všech ikon.

Tomu se dá předejít tím, že sprite uděláte vždy o něco větší, takže budete mít rezervu a navíc můžete sprite zaokrouhlit na co nejvhodnější rozměr (např. pro 25 ikon s mezerami udělat místo 9×9 sprite velký 11×11 a získat místo pro dalších 11 míst pro budoucí ikony).

Druhou možností je už vytvořený sprite nechat jak je a přidat další sprite pro nové ikony. To má tu výhodu, že prohlížeč nebude stahovat zbytečně velké sprity a zobrazí alespoň část ikon dříve. A samozřejmě vy nemusíte přepočítávat pozice ikon.

1 komentář u „Dynamicky zvětšované sprite“

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

Tato stránka používá Akismet k omezení spamu. Podívejte se, jak vaše data z komentářů zpracováváme..