CSS sprite-ok: Halálcsók a képdarabolásnak

Írta: Dave Shea

Mikor még a videójátékok jó szórakozásnak számítottak (itt most a 8 bites aranykorról beszélek), a grafika szükségképpen egyszerűbb problémának számított. A kétdimenziós bittérkép karakteradat és a háttérkörnyezet külön-külön lett kirajzolva, mint a mostani pixel művészet. A sprite-oknak hívott apró grafikák százai, majd ezrei építették fel a játékok képeit.

example sprites

Ahogy a játékok egyre bonyolultabbak lettek, a különböző technikákat fejlesztettek ki a minél több sprite kezelésére. Az egyik variáció a sprite-okat (korabeli fordítási próbálkozás szerint manók – a ford.) egy rácsba illesztette, amiből szükség esetén kiemelte, és a képernyőn megfelelő helyre másolta.

Mit lehet ezzel kezdeni a weben?

Mindennek eljön a reneszánsza, és bár a 3D játékok felemelkedése fölöslegessé teték a sprite-okat, a mobileszközök párhuzamos felemelkedése újra divatba hozta a 2D játéktechnikákat. És most egy kis számolgatással és sok-sok CSS-sel a web világába hozzuk az alapkoncepciót.

Kifejezetten a régimódi kép-feldarabolásos módszert (valamint a JavaScript szükségességét) fogjuk CSS megoldásra cserélni. Ám a CSS működését tekintve tovább is lépünk: a képekből rácsot hozunk létre, kieszelve a képből minden egyes cella kivágását, egyben tárolhatunk minden nyomógombot, navigációs elemet, és minden mást is, amit eddig külön fájlba tettünk. Ezzel minden „előtte” és „utána” hivatkozás állapot is meg tudunk valósítani.

Hogyan működnek a CSS sprite-ok?

Ahogy az majd ki fog derülni, a szükséges technológia már eleve bele van építve a CSS-be, ha egy kicsit kreatívan gondolkodunk.

Kezdjük magával a mesterképpel. Osszunk négy elemre egy téglalapot, mint például ezt a mesterképet, ami a felső sorban a mi „előtte” képeket, az alsó sorban az „utána” (a :hover) képeket tartalmazza. Nincs elválasztás a négy kép között, így csak képzeljük azt, hogy minden szöveg egy-egy link (az egyszerűség miatt a link képeket ezután is „előtte” képeknek, a :hover képeket pedig „utána” képeknek fogjuk hívni. Ezzel lehetőség nyílik a technikát :active és a :visited linkállapottal is kibővíteni, de most ebbe ne menjünk bele).

Akiknek ismerős Petr Stanicek (Pixy) Gyors képcsere technikája, az már tudja, mit is fogunk csinálni. E cikk sok hálával tartozik Pixy példájának, amin ez a módszer is alapszik, de ne rohanjunk előre.

Aztán vagyü a HTML-t. Minden jó CSS trükk egy tiszta HTML-re próbál építeni, és ez a technika sem kivétel:

  <ul id="skyline">
    <li id="panel1b"><a href="#1"></a></li>
    <li id="panel2b"><a href="#2"></a></li>
    <li id="panel3b"><a href="#3"></a></li>
    <li id="panel4b"><a href="#4"></a></li>
  </ul>

Ez a kód adja példánk alapját. Egyszerű, pehelysúlyú jelölés, ami megjelenik a régi, CSS-nélküli böngészőkben is, és ez a trend jó az iparágnak is. Erre bátran alapozhatunk. (Most eltekintünk a linkek szöveges tartalmától. Használd a kedvenc képre cserélő technikádat a linked tartalmának elfedésére.)

Írjunk rá CSS-t!

Itt az ideje, hogy a megadott építőkövekből létrehozzuk a CSS-t. Kezdés előtt egy kis kitérő: egy IE hiba miatt az „utána” képet az „előtte” kép főlé helyezzük, és nem csak kicseréljük. Az eredmény nem okoz semmiféle látványbeli különbséget, de ez a megoldás legalább nem okozza a kellemetlen hazárd effektust, amit nem szeretünk (hazárd: az egyik állapotból a másikba váltás zavaró villanással történik. A bonyolult digitális vezérléseket is szükséges hazárdmentesíteni – a ford.).

  #skyline {
    width: 400px; height: 200px;
    background: url(test-3.jpg);
    margin: 10px auto; padding: 0;
    position: relative;}
  #skyline li {
    margin: 0; padding: 0; list-style: none;
    position: absolute; top: 0;}
  #skyline li, #skyline a {
    height: 200px; display: block;}

A józan észnek látszólag ellentmondva nem a hivatkozásokhoz rendeljük a képeket, hanem az <ul>-hez. Mindjárt meg is látjuk, miért.

Az előző példa további CSS része olyanokat állít be, mint a #skyline blokk és a listaelemek dimenziói, a listaelemek kezdő pozíciói, és kikapcsolja a lista fölösleges felsorolásjeleit.

Magukat a linkeket üresen, átlátszónak hagyjuk (megadott dimenzióval), hogy kiválthassák a link aktivitást, és az azokat körülvevő <li>-k pedig pozícionálnak. Ha magukat a linkeket pozícionáltuk volna, és kihagytuk volna a <li>-ket, néhány régebbi böngésző hibáját csaltuk volna csak elő, szóval olyat inkább ne tegyünk.

De hát a <li>-k abszolút pozícionálásúak, miért nem a böngésző bal felső sarkában jelentek meg? Mert a pozícionális elemek remek tulajdonsága, hogy a bennük lévő elemek abszolút pozíciói a szülőelem szélétől érvényesek, és nem a böngészőablak szélétől. Ezért került a #skyline-ba a position: relative;, így a <li>-k már a #skyline-hoz képest lesznek csak abszolútok.

  #panel1b {left: 0; width: 95px;}
  #panel2b {left: 96px; width: 75px;}
  #panel3b {left: 172px; width: 110px;}
  #panel4b {left: 283px; width: 117px;}

Eszerint a #panel1b nincs vízszintesen eltolva, a #panel2b 96 pixellel van eltolva a #skyline-hoz képest balról, és így tovább. A linkekhez hozzáírtuk a display: block;-ot, és ugyanakkora magasságot, mint a <li> még az előző kódrészletben, így végül kitöltik a teljes <li> által hagyott helyet, pontosan úgy, ahogy kell.

Ebben a pillanatban van egy egyszerű hivatkozástérképünk (imagemap-ünk) linkekkel, de még nincsenek :hover (lebegő) állapotok. Nézd meg a példát. Talán könnyebben megérthető, hogy mi is történik, ha a keretek be vannak kapcsolva.

Lebegő állapotok

Régebben JavaScript-et kellett használnunk, ha ki akartuk cserélni a képet, mikor fölé húzták az egérmutatót. Nekünk ezek az „utána” képek is ugyanabban a képben vannak, így ki kell találnunk egy módszert, hogy egyenként ki tudjuk tenni azokat a megfelelő linkhez.

Ha a mesterképet további beállítások nélkül hozzárendeljük a :hover állapothoz, csak a bal felső sarkot tesszük láthatóvá (amit nem akarunk), bár a link területbe belevágva, amit pedig igenis akarunk. Most már csak valahogy pozícionálnunk kell a képet.

Ismert pixel értékekkel van dolgunk. Már csak egy kis számtan kérdése az egész, hogy pontosan annyival tolhassuk el a háttérképet mind vízszintesen, mint függőlegesen, amennyire helyére nem kerül az „utána” kép.

Pontosan ezt kell tennünk:

  #panel1b a:hover {
    background: transparent url(test-3.jpg)
    0 -200px no-repeat;}
  #panel2b a:hover {
    background: transparent url(test-3.jpg)
    -96px -200px no-repeat;}
  #panel3b a:hover {
    background: transparent url(test-3.jpg)
    -172px -200px no-repeat;}
  #panel4b a:hover {
    background: transparent url(test-3.jpg)
    -283px -200px no-repeat;}

Hogy honnan vettük a pixel értékeket? Nézzük csak meg közelebbről: az első érték természetesen a vízszintes eltolás (a bal széltől), a második meg a függőleges.

Minden függőleges érték egyenlő. Mivel a mesterkép 400 pixel magas, és az „utána” kép az alsó felében van, egyszerűen kétfelé osztottuk a magasságot. A teljes háttérkép 200 pixellel feltolásához negatív értéket kell használnunk. Gondolj a tetejére, mint a link kezdőpontjára, ami kezdetben 0. Az ettől 200 pixellel feljebb lévő pont -200-nál helyezkedik el.

Ugyanígy, ha a bal oldali link a 0-nál helyezkedik el, minden <li>-nél annyival előrébb kell helyeznünk az „utána” képet, amennyivel hátrébb van a link. Így az első linknek nincs szüksége eltolásra, mivel nincs előrébb további kép. A másodiknak már az első szélességének megfelelő eltolásra van szüksége, a harmadiknak az első és második kép szélességének megfelelő, és így tovább.

Kicsit fárasztó elmagyarázni a folyamatot, de kis gyakorlással rá lehet érezni a dologra, és nem is tűnik már annyira ördöngősnek.

Nézd meg, mire jutottunk. Egyszerű képcsere, ami egyszerű, számozatlan listaként működik CSS nélkül.

Nyomógombok

Egyáltalán nincs szükség arra, hogy a hivatkozások egymást érjék, mint az első példában. Hivatkozástérkép is jó egy-két helyen, de mi van akkor, ha a hivatkozásokat különálló nyomógombonként kívánjuk megjeleníteni? Így adhatunk nekik keretet és margót, az alsó hátteret átlátszóvá tehetjük, szóval teljesen különálló elemként kezelhetjük.

Valójában az építőelemek már a helyükön vannak. Nem akarjuk a kódot túlzottan módosítani. A legfontosabb változtatás egy új háttérkép formájában történik, amit nem egybefüggően helyezünk el. Mivel most nem használhatjuk az <ul>-t az eredeti háttér kirajzolásához, minden egyes <li>-vel fogjuk kirakni inkább, egyenként elcsúsztatva a képet, mint az előző példában.

Egy megfelelő képpel és a <li>-k között egy kis távolságot hagyva nyomógombokat kapunk.

Figyeljünk arra, hogy ebben a példában egy 1px-es keretet adtunk a gombokhoz, amik természetesen tovább növelték a hivatkozások szélességeit. Ez befolyásolta az eltolás mértékét is, amit a szükséges helyeken 2px-es eltolásnöveléssel ellensúlyoztunk.

Szabálytalan formák

Egész idáig négyszögletes, egymást nem fedő alakzatokkal dolgoztunk. Mi van azonban a bonyolultabb hivatkozástérképekkel, amiket a Fireworks és az ImageReady olyan könnyen legenerál? Nyugalom, most erre is sort kerítünk.

Kezdjük úgy, mint az előző példát: tegyünk az <ul>-be egy háttérképet, kapcsoljuk ki a felsorolásjeleket, állítsuk be a szélességeket, és így tovább. A nagy különbség a <li>-k pozícionálásában van. A cél az, hogy minden grafikus elemet fedjünk le szorosan.

Tehát mégegyszer: mivel az abszolút pozícionálás az <ul> bal felső sarkához van mérve, oda helyezzük a linkeket, ahova csak kedvünk tartja. Így hát csak annyi van hátra, hogy hozzá írjuk a lebegő állapotokat.

Ebben az esetben a legnehezebb az, hogy nem elég egy „utána” kép: az egymást átfedő objektumok miatt a csak egy „utána” állapot megjelenne a környező objektumok átfedéseinél is (egyszerűbb, ha erről saját szemeddel is meggyőződsz).

Hogy lehet ezt elkerülni? Csak készíts még egy „utána” képet, és óvatosan válaszd meg, hogy melyik objektum hova kerüljön. Ebben az esetben a mesterképen az első „utána” képre kerültek a bíbor és a kék objektumok, míg a zöld, narancs és sárga objektumok a másodikra. Ezzel a sorrenddel mindegyik objektum „utána” állapotát úgy lehet kivágni, hogy a környező objektumok „előtte” állapotban legyenek. Ezzel az illúzió tökéletes lesz.

Előnyök és buktatók

Néhány záró gondolat. Az új CSS Sprite módszerünk remekül működik a legtöbb modern böngészővel. Említésre méltó kivétel az Opera 6, ami a lebegő állapotú hivatkozáshoz nem enged háttérképet megadni. Nem tudjuk miért, de a lényeg, hogy a képcserélés ott nem működik. A hivatkozások persze mennek, és ha rendesen be vannak állítva a méretek, egy bár statikus, de használható hivatkozástérképet kapunk. Valószínűleg nem fogunk rosszat álmodni emiatt, hiszen a 7-es Opera már egy ideje létezik.

A másik probléma ismerős lehet azok számára, akik már próbálkoztak képcserélő eljárásokkal. Néha, mikor az olvasó kikapcsolja a képeket a böngészőjében, csak nagy üres teret fog látni, ahol mi a képeinket akartuk volna elhelyezni. A linkek még ott lesznek és rájuk is lehet kattintani, de semmi látható nem fog megjelenni. Ezidáig semmiféle megoldást nem találtak erre.

Aztán itt van a kép hossza. Természetes tendencia, hogy azt feltételezik, egy teljes kétszer akkora kép hosszabb, mint a feldarabolt képek külön-külön, hiszen a teljes képméret rendszerint nagyobb. Minden képformátumnak van egy kis adatfölöslege (ezért lesz az 1x1-es GIF-ből 50 bájt), és minél több képed van, annál több ez a fölösleg. Ráadásul egy mesterképhez csak egy színtáblázat szükséges GIF esetén, míg a feldarabolt képeknél ezek is ismétlődnek. Az előzetes vizsgálat tehát azt sugallja, hogy CSS Sprite-oknál rövidebb, vagy legalábbis nem jelentősen hosszabb lesz a képméret.

Végül ne felejtsük el, hogy a HTML jelölés szép és tiszta lesz, pont ami felé szeretnénk haladni. A HTML listák gyönyörűen listák lesznek CSS nélkül, és egy megfelelő képcserélő technika a képolvasók számára is láthatóként hagyja a hivatkozás szövegeket. A Sprite képek cseréje is holt egyszerű, mivel minden méret és eltolás egy CSS fájlban van, és minden képelem egy nagy képben van tárolva.

Szólj hozzá!

Számodra is hasznos ez a technika? Szólj hozzá.