CSS Sprites

Gdy łączymy się ze stroną, przeglądarka wysyła do niej żądania (HTTP request) o pobranie różnych plików – grafik, skryptów itp. Następnie otrzymuje zwrot w postaci odpowiednich danych. Tak to wygląda w największym skrócie. Im więcej takich requestów, tym logicznie nasza przeglądarka musi wysłać z domu do serwera więcej requestów, i tym samym strona ładuje się wolniej.

My jako webmasterzy powinniśmy starać się by takich połączeń było jak najmniej.

Optymalizacja liczby requestów

Przede wszystkim ograniczamy liczbę plików JS, CSS i grafik.
Te pierwsze warto pakować we wspólne biblioteki (np skrypty odpowiedzialne za obsługę formularzy wrzucamy w jeden plik). Dodatkowo kod tych skryptów możemy spakować np za pomocą http://dean.edwards.name/packer/.

Style jak to style. Ta sama technika co powyżej. Niektórzy uważają, że CSS nie muszą być czytelne – wtedy warto je spakować np za pomocą http://www.minifycss.com/css-compressor/ (takich kompresorów jest bardzo dużo – wystaczy w google wpisać „css minimizer”). Ja osobiście wolę jednak style bardziej czytelne, bo łatwiej je potem debugować. Co człowiek to inne podejście.

Oczywiście powyższe rady warto stosować z głową. Jeżeli piszę, żeby pliki js pakować we wspólne biblioteki, to nie znaczy to, że od razu koniecznie musimy wszystko spakować do jednego pliku. Tak samo w przypadku CSS. Nie jest jakimś złem używanie komendy @import. Używanie jej jest nawet całkiem dobre, bo dzięki niej można bardzo fajnie uporządkować sobie style, rozbijając je na pliki (np style do formularzy, style inputów, style layoutu itp). Pamiętajcie, żeby się nie zatracać w optymalizacji pomijając wszystko inne.

CSS Sprites

W przypadku grafiki stosuje się technikę zwaną CSS sprites. Polega ona na wrzuceniu grafik do wspólnego PNG, a następnie za pomocą CSS pobierania odpowiednich części tak przygotowanego pliku. Proste w teorii, ale jak się zaraz przekonamy proste i w praktyce.

Sprite youtube

Powyższa grafika to sprite ze strony Youtube. Ile tam mamy oddzielnych grafik? 30? 50? Ciężko nawet zliczyć. Umieszczając je wszystkie na jednej wspólnej grafice redukujemy liczbę requestów z > 50 do jednego.

My zaczniemy od nieco prostszej wersji sprita :)

Aby pobrać odpowiednie części za pomocą CSS wystarczy skorzystać z właściwości background-position.
Pobierzmy na przykład ikonę klucza. Znajduje się ona w pozycji x:137 y:12 i ma wymiary 46×46.

.wrench {
     display: inline-block;
     overflow: hidden;
     width: 46px;
     height: 46px;
     background: url(sprite.png);
     background-position: -147px -12px;
     background-repeat: no-repeat;     
}

Ujemne wartości przy pozycji tła przesuwają tło w odpowiednie miejsce. overflow:hidden sprawia, że element przycina wszystko co jest poza nim (także tło). Zresztą co ja będę wam tłumaczył. Jedna animacja zastąpi tysiąc słów:

I w zasadzie jest to cała zasada działania. Opis wszystkich ikon z zastosowaniem skróconych właściwości css wygląda tak:

.icon {
     background: url(sprite.png) no-repeat;
     overflow:hidden;
     display:inline-block;
}
.icon.red-dot {
     background-position:-12px -12px;
     width:43px;
     height:43px;
}
.icon.green-dot {
     background-position:-72px -12px;
     width:43px;
     height:43px;
}
.icon.wrench {
     background-position:-137px -12px;
     width:46px;
     height:45px;
}
.icon-alert {
     background-position:-195px -12px;
     width:46px;
     height:41px;
}

Wycinki sprita

W powyższym przykładzie mieliśmy na sztywno ustalone rozmiary naszych stylowanych obiektów. Dzięki temu overflow:hidden sprawiał, że tło poza elementem nie było wyświetlane. W wielu przypadkach szerokość elementu nie będzie znana. Spowoduje to, że jeżeli umieścimy na nim naszą ikonę, tuż obok niej pojawi się dalsza część sprita czyli inne ikony. Jednym słowem lipa.

Aby móc pobierać tylko dany fragment sprita, skorzystamy z metody background-clip, która dobrze jest opisana tutaj. Niestety nie działa ona na starszych przeglądarkach z IE8 włącznie, dlatego poniżej pokażę wam też inne sposoby.

Jednym z rozwiązań tego problemu jest ułożenie grafik na spricie jedna pod drugą. To samo tyczy się teł, które będą powtarzane w poziomie.

Element :before, :after dla spritów

Jest jeszcze inna technika, aby obejść powyższą niedogodność. Wystarczy skorzystać z selektorów :before lub :after, dzięki którym możemy wstawiać do dokumentu pseudo elementy. Takie pseudo cudaki możemy ostylować tak samo jak w powyższym przykładzie:

.icon-button {
     position: relative;
}

.icon-button:before {
     content:'';
     display:block;
     background: url(http://domanart.pl/wp-content/uploads/2012/08/sprite1.png) -195px -12px no-repeat;
     overflow:hidden;
     width:46px;
     height:41px;
     position:absolute;
     top:5px;
     left:5px;
}

http://jsfiddle.net/kartofelek007/Ryj5u/

Pseudo element wstawiony za pomocą :before pozycjonujemy absolutnie względem jego „rodzica”, który ma pozycjonowanie relatywne (chociaż w tym przypadku ciężko mówić o rodzicu, bo elementy before i after to takie bękarty html – w końcu to shadow dom).
Musimy mu też nadać tryb wyświetlania block. Reszta stylowania jest taka sama jak w poprzednich przykładach. Przy właściwości content: lepiej używać apostrofów podwójnych, niż pojedynczych. Zauważyłem, że IE zupełnie głupiał przy pojedynczych (chociaż ogólnie starsze IE z pseudo elementami nie za bardzo się lubią, więc cała ta technika nie za bardzo pasuje dla starych IE).
Najpiękniejsze w tej metodzie jest to, że taki pseudo element możemy „obrabiać” tak samo jak każdy element na stronie. Możemy mu przesuwać tło, przemieszczać (top, left), skalować, obracać, czy stosować inne transformacje css. W poniższym przykładzie ikonkę obróciłem do góry nogami (chociaż tak właściwie to ona nie ma nóg…)

http://jsfiddle.net/kartofelek007/7H3nd/

Tworzenie spritów

Sprity zazwyczaj tworzymy w naszym ulubionym programie graficznym :)
W Photoshopie najlepiej użyć do tego narzędzia slice.

.

Za jego pomocą wyznaczamy kolejne części sprajta.

Następnie za pomocą narzędzia slice select (patrz poprzednia grafika) możemy 2x kliknąć w któryś z wyznaczonych obszarów, aby w okienku, które się ukaże zobaczyć podane wymiary i położenie danego obszaru (oczywiście x i y przestawiamy na ujemne).

Jest to duże ułatwienie w porównaniu z używaniem standardowego zaznaczania i panelu info w Photoshopie (ileż ja się nakląłem używając tej standardowej metody).

Inne narzędzia

Ale, ale. Tworzenie spritów w programach graficznych nie jest ani super przyjemne, ani wygodne.
Dlatego też powstało sporo narzędzi przeznaczonych do tego celu.

Wśród internetowych aplikacji warto zwrócić uwagę na:

Wśród aplikacji na komputer jedną z lepszych jest http://spriterightapp.com/, niestety przeznaczony tylko dla ludzi miłujących się w jabłkach.

Dla zwykłych śmiertelników też coś można wygrzebać. Osobiście polecam zapoznać się z dwoma aplikacjami:
http://www.jonathanrowny.com/journal/sprite-hero-yet-another-css-sprite-generator oraz http://collamo.jp/?page_id=95. Każda z nich ma swoje plusy i minusy, dlatego warto przetestować obie.

Dodatkowe informacje na temat spritów znajdziecie na http://coding.smashingmagazine.com/2012/04/11/css-sprites-revisited/.

Jeżeli macie jakieś pytania, zapraszam do komentowania :)

Komentarze

  • Pingback: :after i :before - doman.art.pl()

  • Po pierwsze wydaje mi się, że „overflow” ma niewiele wspólnego z przycinaniem „backgrounda” :)
    Po drugie warto podlinkować to narzędzie do cięcia sprite’ów ;)
    http://spritecutie.com/

    Pozdrawiam,
    Tanner

    • kartofelek007

      Chodzi o to, że jak nie zastosujesz tej właściwości, to wtedy content rozepchnie ci rozmiary boxa, co w wielu przypadkach zadziała na twoją niekorzyść. Link dodany.

      • Skoro ustawiamy „width” i „height” to kontent nie rozepchnie tychże rozmiarów. Może co najwyżej poza nie wystawać – i to jest uzasadnienie „overflow”. Zresztą, tak jak napisałeś w nowszym wpisie o cięciu layoutu i zerowym zorientowaniu grafika – trzeba wyróżnić fakt, że jeżeli używamy stałych sprite’ów to musimy też stałą ilość tekstu wsadzać i to dalej obala przycinanie „backgrounda” „overflowem”.
        I tak dalej…

        Pozdrawiam,
        Tanner

      • kartofelek007

        Masz rację. Content będzie wystawał poza taki box, ale jego rozmiary się nie zmienią. Teraz ustawiasz np 20px ikonkę jako tło i masz taki przypadek:
        http://cssdeck.com/labs/wzji1nzk
        Tak czy siak lipa. Ale rację masz. Overflow nie jest potrzebny. Jest tylko dodatkowym zabezpieczeniem (nie zawsze dobrym).

      • Dobra dobra :D Jeżeli już zagłębiamy się w temat tego „overflowa” to sprawa jest prosta: trza się zastanowić czy to ma dopasowywać się do tekstu czy może być statyczne. W tym wpisie stricte opisujesz przypadek statyczny.
        A tak swoją drogą: 5zł za sprostowanie :D

        Pozdrawiam,
        Tanner

      • kartofelek007

        No niestety biedny ze mnie web majster. A raczej leniwy :) Stąd niedopatrzenia itp, o których już kiedyś w komentarzach wspominałem. Jak to się nazywa? Zaufanie do czytelników, którzy w mojej opinii mają swoją wiedzę, a to co czasami opisuję sami wykorzystają na swój sposób, modyfikując i usprawniając. Bla bla bla.

  • Pingback: Stylowanie formularzy - doman.art.pl()

  • Comandeer

    „Nie jest jakimś złem używanie komendy @import. ”

    Właśnie w tym sęk, że jest ;) http://www.stevesouders.com/blog/2009/04/09/dont-use-import/

    Co do sprites, kiedyś spotkałem się z mega fajną techniką w SVG (szkoda, że wciąż nie jest wspierana, co jest IMO kpiną jawną) http://comandeer.pl/tutorials/?id=svg-sprites

    Natomiast co do dzielenia kodu na pliki: od kiedy pojawił się grunt dla node.js, że tak powiem robi się to samo ;) CSS generowany z LESS-a, to przepuszczane przez Autoprefixer i minifikowane do pliku. tak samo z JS. w czasie developingu mam zupełnie czysty i podzielony kod CSS, jedynie na produkcję trafia wersja skrajnie zoptymalizowana (skoro mogę oszczędzić te kilkanaście/dziesiąt kB, to czemu mam tego nie zrobić?)

    • kartofelek007

      Bardzo często webmasterzy boją się tego jednego importu, czy requesta równocześnie serwując np 2x za duże pliki bo ich nie zoptymalizowali, czy np waląc na stronę pełno pluginów typu facebook, itp które same w sobie dodają kilkanaście requestów.

      To są teorie, tak samo jak np „rób bicepsa na 8 powtórzeń”. Jeżeli strona jest bardzo zoptymalizowana to walka o taki jeden request ma sens. Inaczej to średnio to widzę, szczególnie że bardzo często taki ręcznie pisany kod przypomina (mówimy o kilku tysiącach linii) śmietnik.

      W LESS sprawa wygląda bardziej przyjemnie, ale osobiście do tej pory nie znalazłem metody by sprawdzał się on w pracy grupowej (w sensie jego debugowanie po spakowaniu).

      • Comandeer

        Problem w tym, że najczęściej CSS (zwłaszcza [media=screen]) to blocking resource i browser leci w kulki dopóki go nie ściągnie. a wszystkie widgety de facto lecą asyncem, więc istnieje tutaj pewna różnica.
        Jeśli kod przypomina śmietnik, to IMO jest to wina programisty ;) a wsparcie dla SVG powinno być obecnie o wiele lepsze… i tak dobrze, że jest lepsze od tego dla MathML…

        Osobiście nigdy nie spotkałem się z problemem przy debugowaniu CSS – inspektor w Chrome zazwyczaj starcza. Jeszcze nie jestem na etapie jednostkowych testów dla CSS i source maps ;)

  • Pingback: :after i :before - domanart.pl()