nbw: Redefine the undefined

Icon

O Sieci, standardach i róznych takich…

Zoptymalizuj duszka – CSS Sprites zoptymalizowane

W życiu każdego popularnego serwisu internetowego przychodzi czas, kiedy należy zabrać się za jego optymalizację. Zwykle dokupuje się dodatkowe serwery, wpina memcache, optymalizuje kod serwisu i zapytania do bazy danych. Frontend jest zwykle pomijany, choć w rzeczywistości, optymalizując szablony, można uzyskać całkiem niezłe wyniki relatywnie niskim nakładem sił i środków.

Ponieważ miałem ostatnio kilka serwisów, które trzeba było zoptymalizować, postanowiłem przypomnieć sobie technikę CSS Sprites opisaną przez Dave’a Shea na łamach AListApart. Jeśli ktoś jej nie zna – proponuję się zapoznać. To prosta technika, z korzeniami w świecie gier komputerowych, polegająca na łączeniu wielu plików w graficznych w jeden większy, łatwiejszy w zarządzaniu i zmniejszający ilość zapytań o elementy graficzne.

Niedawno przetestowałem to rozwiązanie w kilku serwisach, które aż prosiły się o optymalizację i trafiłem na ciekawe zachowanie. Ale po kolei. Zakładając, że nasz przeciętny Jan Kowalski, czyta artykuł na ALA po raz pierwszy, skorzysta z zapisu zaproponowanego w artykule:

#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;}

Puryści i fanatycy optymalizacji już w tym momencie powinni (już możecie) złapać się za głowę. Ale to nie nieoptymalny zapis jest problemem; prawdziwy problem rodzi się, kiedy chcemy skorzystać z tego rozwiązania w serwisie, w którym nasz plik z “duszkami” ma ponad 350kb, przeciętny obiekt wycinany z pliku ma wymiary rzędu 250×160px a takich wycinanek mamy na jednej podstronie około dwudziestu.
W przeciwieństwie do demonstracyjnego pliczku (parę kb) załadowanie takiego potworka chwilę zajmuje i – jak się okazuje – wpływa na sposób budowania strony… w Firefoxie.

Chcieliście napisać: no jasne, że w Internet Explorerze? Właśnie nie. Ten – o dziwo – renderuje stronę tak, jak byśmy chcieli: po załadowaniu pliku, pojawiają się wszystkie elementy graficzne. Od razu. Ale nie w Firefoxie; Firefox wycina je po kawałku tak, jak by nic się nie zmieniło.

Ok, co jest źle z tym zapisem? Przede wszystkim: zupełnie zbędnie powtarza się ten sam kawałek kodu tylko po to, by zmienić jedną/dwie wartości. Deklarację tła i jego powtarzanie możemy przenieść w jedno miejsce tak, by później tylko deklarować co chcemy wyciąć z tego pliku. Poprawniejszy zapis, może wyglądać tak:

#panel1b, #panel2b, #panel3b, #panel4b {
background: url(test-3.jpg);
background-repeat: no-repeat;
}
panel1b {
background-position: 0 -200px;
}

…

I to jest rozwiązanie warte kilka sekund renderowania strony. Firefox nie lubi się z powtarzaniem całej deklaracji ładowanego tła – za każdym razem otwiera plik z grafiką, wycina i wkleja potrzebny fragment wydłużając cały proces w swoistą “nieskończoność”. W moim przypadku, zmieniając deklarację udało mi się zejść z 16-20s renderowania strony (wg YSlow) do 6.

Powiązane wpisy:

  1. CSS, Opera i Firefox: Z kamerą wśród przeglądarek (wciąż je psuję)..

Category: błędy, projektowanie, www

Tagged:

7 Responses

  1. klucznik says:

    firefox nie lubi powtorzonej deklaracji tla z wylaczonym cachem, pobiera wtedy sprita wiele razy.

    Ten wynik byl mierzony z cachem czy bez ?

  2. riddle says:

    Warto jeszcze pamiętać, aby nie przesadzić z ilością sprite’ów w pliku – jeśli są to ikony bądź gradienty, to nie ma problemu, natomiast jeśli ładujemy w niego wszystkie wielkie grafiki (dla divów z zaokrąglonymi rogami np), to finalny plik może przekroczyć parę tysięcy pikseli.

    Ktoś pewnie zapyta, jaki w tym problem, przecież whitespace jest za darmo, kompresja w PNG działa, a plik zajmuje raptem 20KB. Tak, ale na dysku. Ten plik jest wyświetlany jako bitmapa, a ta może sięgnąć nawet kilkudziesięciu MB (w przypadku takich ogromnych przestrzeni). Zaoszczędzimy więc na wadze i HTTP requests, natomiast obciążymy przeglądarkę usera dodatkowymi megabajtami i zeżremy solidnie CPU. I nie są to czcze słowa, ktoś to przetestował.

    Dlatego – stosujmy sprite’y rozsądnie i tam gdzie warto.

  3. nbw says:

    A o tym będzie w kolejnej części bajki. Gdzieś w okolicach gwiazdki.

    Przyszłego roku.

  4. nbw says:

    @klucznik FF nie pobiera sprajta wiele razy z sieci, on go za każdym razem otwiera i zamyka w pamięci. Pewnie ktoś z ekipy mozilli mógłby fachowo określić co się dzieje. Efekt jest taki, że każdy element w którym pojawia się deklaracja tego samego pliku jest odrysowywany niezależnie

  5. mlen says:

    dodam tylko, że zoptymalizowałbym własność jeszcze bardziej – i zamiast:
    background: url(test-3.jpg);
    zapisałbym:
    background-image: url(test-3.jpg);

  6. riddle says:

    (Teraz następuje mierzenie wirtualnych końcówek)

    background: url(test-3.jpg) no-repeat; wydaje się być najoptymalniejsze. background-position niżej nadpisze domyślne ‘0 0′ pominięte we wspólnej deklaracji.

  7. nbw says:

    W zasadzie tak, macie rację (wyszedłem już trochę z wprawy). Najważniejsze by w FF przyspieszyć.

Leave a Reply