• Strona główna
  • Curriculum Vitae
  • O mnie
  • Mapa strony
  • Kontakt
Niebieski Pomarańczowy Zielony Różowy Fioletowy

Konwersja JS i CSS do PNG

Opublikowane 27 sierpnia 2010. Autor: Kamil Brenk. Wizyt: 1 182.

Kategorie: Front-end Development
Tematyka: CSS, grafika w Internecie, JavaScript, kompresja gzip, Minify CSS, Minify JavaScript, optymalizacja serwisów, programowane ciekawostki, wydajność serwisów internetowych

sie 27

Kompresja JavaScript oraz kompresja CSS może znacząco przyśpieszyć wczytywanie się strony, co jest szczególnie ważne dla osób korzystających z wolnych łącz (pozdrawiam tutaj wszystkich klientów Asta-Net, którzy płacą ponad 50 zł/m-c i mogą ściągać z prędkością 5 KB/s :D, do których niestety też się zaliczam).

We wpisie tym pokrótce zaprezentuję bardzo ciekawą metodę kompresji plików JavaScript oraz CSS z wykorzystaniem algorytmu deflate (dla plików PNG) oraz technologii Canvas.

Najciekawszy w tym rozwiązaniu jest sposób przechowywania skompresowanych danych. Otóż kod JavaScript, tudzież CSS (czy jakikolwiek inny) jest konwertowany do obrazu PNG (który jak wiadomo jest bardzo dobrze spakowany), co znacząco zmniejsza wielkość pliku.

A więc najpierw pakujemy nasze pliki do PNG. Następnie wczytujemy stronę, odkodowujemy plik PNG (mniejszy o kilkanaście, czasem nawet kilkadziesiąt procent) i wykonujemy po stronie przeglądarki. Prosto, szybko i przyjemnie!

Praktyczny przykład

A więc najpierw musimy użyć jakiegoś narzędzia do kompresji JavaScript (czy CSS). Następnie przystępujemy do zapisu naszych spakowanych plików do formatu PNG. Możemy napisać własny skrypt PHP z wykorzystaniem biblioteki GD, możemy także skorzystać z gotowców.

ruby:
http://gist.github.com/542462

php:
http://github.com/iamcal/PNGStore

Wygenerowany w ten sposób plik może wyglądać następująco:

  • jquery-1.2.3.min.js (17 KB)
  • prototype-1.6.0.2.js (30 KB)

Jak więc widać, jest to zwykły plik PNG możliwy do otwarcia w dowolnej przeglądarce graficznej.

Następnym krokiem jest zamieszczenie prostej funkcji na naszej stronie, który zajmie się „odkodowaniem” stworzonego pliku PNG:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
 * Load PNG Data
 * Source: http://www.nihilogic.dk/labs/canvascompress/pngdata.js
**/

function loadPNGData(strFilename, fncCallback) {
    // test for canvas and getImageData
    var bCanvas = false;
    var oCanvas = document.createElement("canvas");
    if (oCanvas.getContext) {
        var oCtx = oCanvas.getContext("2d");
        if (oCtx.getImageData) {
            bCanvas = true;
        }
    }
    if (bCanvas) {
        var oImg = new Image();
        oImg.style.position = "absolute";
        oImg.style.left = "-10000px";
        document.body.appendChild(oImg);
        oImg.onload = function() {
            var iWidth = this.offsetWidth;
            var iHeight = this.offsetHeight;
            oCanvas.width = iWidth;
            oCanvas.height = iHeight;
            oCanvas.style.width = iWidth+"px";
            oCanvas.style.height = iHeight+"px";
            var oText = document.getElementById("output");
            oCtx.drawImage(this,0,0);
            var oData = oCtx.getImageData(0,0,iWidth,iHeight).data;
            var a = [];
            var len = oData.length;
            var p = -1;
            for (var i=0;i<len;i+=4) {
                if (oData[i] > 0)
                    a[++p] = String.fromCharCode(oData[i]);
            };
            var strData = a.join("");
            if (fncCallback) {
                fncCallback(strData);
            }
            document.body.removeChild(oImg);
        }
        oImg.src = strFilename;
        return true;
    } else {
        return false;
    }
}

Funkcja ta przyjmuje dwa parametry: ścieżkę do obrazu PNG oraz funkcję callback, która zostanie wywołana po prawidłowym załadowaniu obrazu. Tak może wyglądać nasz przykład:

1
2
3
4
5
6
7
8
9
loadPNGData('jquery.min.js.png', function(d) {

    eval( d );
   
    $(function() {
        alert('its work!');
    });
   
});

Co tutaj się dzieje? Najpierw odbieramy nasz „obrazek” (w którym jest zakodowana biblioteka jQuery) przy pomocy funkcji loadPNGData, tworzymy nowy obiekt Canvas, operując na obrazie odkodowujemy piksel po pikselu zakodowany kod, przekazujemy go do funkcji callback, wykonujemy kod przy pomocy funkcji eval (jest to jednoznaczne z „wczytaniem” biblioteki jQuery) – no i od tej pory możemy korzystać z załadowanego skryptu.

Prawda, że proste? :-) Równie dobrze w takim zakodowanym pliku może być kod CSS, dla którego należałoby stworzyć nowy element STYLE w drzewie DOM dokumentu i go tam zagnieździć, co też nie jest problemem.

Zalety

Główną zaletą, o której już wcześniej wspominałem, jest spadek wielkości skompresowanego pliku o kilkanaście, a czasem nawet kilkadziesiąt procent.

Jak działa taka kompresja? Otóż wyczytałem, że w plikach PNG jest stosowana kompresja DEFLATE (pisałem co nieco o kompresji przy pomocy mod_deflate). Ten sam algorytm jest wykorzystywany przez GZIP oraz kompresję HTTP.

A więc kolejną zaletą jest możliwość korzystania z kompresji deflate, nie zważając na to, czy przeglądarka obsługuje GZIP, czy na serwerze nie zostało zablokowane wysyłanie skompresowanych plików (lub po prostu nie zainstalowano modułu mod_deflate).

Wady

Niestety powyższa kompresja ma jedną zasadniczą wadę: nie wszystkie przeglądarki obsługują obiekt Canvas i metodę getImageData. Jeśli nie obsługują to nasze pliki wcale nie zostaną wykonane.

A najgorsze w tym, że Canvas jako dość nowa technologia nie jest wspierana przez niektóre przeglądarki oraz urządzenia (zwłaszcza starszy software oraz przeglądarki mobilne).

Kolejną wadą jest konieczność „odkodowania” pliku PNG przez JavaScript (zajmuje się tym funkcja loadPNGData), co niestety jest dość obciążające dla procesora naszego klienta. Myślę, że będzie to mniej wydajnym rozwiązaniem, aniżeli rozpakowanie pliku przez przeglądarkę (gdyby został spakowany po stronie serwera w GZIP).

Podsumowanie

Chciałem w tym wpisie przedstawić krótko bardzo fajną ciekawostkę – dla mnie to coś zupełnie nowego i interesującego. Choć nie jest to rozwiązanie odpowiednie do komercyjnych aplikacji, jednak dobrze znać taki sposób kompresji plików JavaScript czy CSS.

Podobne wpisy

  • Minimalizacja zapytań HTTP
  • Kompresja JavaScript
  • Testy optymalizacyjne witryny
  • Kompresja CSS
  • Sposoby wczytywania JavaScript

Komentarze (6)

  1. Michal Wachowski 28 sierpnia 2010

    To jest na prawdę szalone :D

  2. css3.pl 17 września 2010

    Interesujące, ale wady zabijają plusy. Najprościej włączyć gzip, która przeglądarka tego dziś nie obsługuje?

  3. Kamil Brenk 17 września 2010

    Pewnie każda przeglądarka, która pozwala wykorzystać technologię Canvas ma także zaimplementowane algorytmy gzip :-) Już prędzej można spotkać prymitywne serwery, na których jest wyłączony gzip i nie możemy z niego skorzystać (republika.pl, zapewne webpark, darmowe serwery i inne).

  4. luq 10 października 2010

    Bardzo ciekawe :)

    Już odbiegając od tego, że canvas nie jest zaimplementowany w IE < 9, ale tak jak piszesz wykorzystanie funkcji eval() jest jednym z najmniej optymalnych rozwiązań. Natomiast to też zależy jakie priorytety mamy, bo jeśli chodzi nam głównie o to aby nie zapychać łącza a nie o to żeby szybciej się wykonały kod po stronie klienta to rozwiązanie jest jak najbardziej okej :)

  5. Piotrek Reinmar Koszuliński 31 lipca 2011

    To rozwiązanie zostało opracowane, jeśli się nie mylę, na potrzeby jakiegoś konkursu typu 10k apart. W tym konkretnym wypadku miało sens, ponieważ, jak się domyślam, gzip nie był brany pod uwagę.

    Jeśli jednak gzip daje taki sam zysk jak kompresja do PNG, to nie widzę sensu używać tego drugiego rozwiązania. Jest niekompatybilne wstecz, nieładne i zapewne dosyć toporne jeśli chodzi o wydajność.

    Ale sam pomysł ryje beret :)

  6. Kamil Brenk 31 lipca 2011

    Zgadza się, to rozwiązanie jest zwykłą ciekawostką, aczkolwiek w konkursach gdzie nikt nie patrzy na wydajność czy wygląd kodu, a liczy się jedynie jak najmniejszy rozmiar (5K Apart, 10K Apart) to takie rozwiązanie nadaje się świetnie :-)



Dodaj komentarz

XHTML: Możesz użyć następujących tagów
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Kamil Brenk Blog

PHP, JavaScript, SQL, HTML

  • Informacje o blogu

    Kamil Brenk

    Blog o tworzeniu aplikacji na potrzeby sieci Web.

    Praktyczne przykłady, porady i sztuczki. PHP, SQL, AJAX, JavaScript, HTML i pochodne.

    Kanał RSS

    • Najnowsze
    • Komentarze
    • Popularne
    • Gramatyka w HTML i CSS
    • PHP kontra Microsoft Office, part I
    • Cross-Domain JavaScript: CORS
    • Wysyłanie wiadomości SMS w PHP
    • Boilerplate 2.0
    • Własne selektory w jQuery
    • Kamil Brenk: @Michał:1) jak już otrzymam dyplom to zrobię serię o...
    • Michal Wachowski: Po pierwsze - tyle czekania i tylko to? A bu! :) Po drugie -...
    • Kamil Brenk: @CapaciousCore: języki kompilowane są szybsze niż...
    • CapaciousCore: @Kamil Brenk wiem, że komentarze i post nie są uber świeże....
    • Kamil Brenk: @CapaciousCore: post i komentarze napisane ponad rok temu;...
    • CapaciousCore: Przebrnąłem przez te wszystkie komentarze i mam trochę...
    • Kamil Brenk: @arhiman: dzięki za komentarz :)A to dziwne co piszesz, bo...
    • Przyszłość PHP
    • Niestandardowe czcionki na stronie
    • Gramatyka w PHP, część 1
    • Umowa i zaliczka dla freelancera
    • Projekt aplikacji po stronie klienta
    • Własny mechanizm Feed
    • jQuery.extends dla PHP
  • Szukajka
    Wpisz co chcesz wyszukać na stronie…
  • Kategorie
    • Apache
    • Freelancer
    • Front-end Development
    • HTML5 & CSS3
    • Inne
    • JavaScript
    • Książki
    • PHP
    • Po godzinach
    • Pozycjonowanie
    • Protokół HTTP
    • SQL
    • Wyrażenia regularne
  • Moje serwisy
    • Testy zawodowe
    • Miłość, uczucia i seks
  • Czytane blogi
    • Wojciech Sznapka
    • Wojciech Soczyński
    • Michał Wachowski
    • Tomasz Kowalczyk
    • JavaScript po polsku | Code42
  • Archiwum
    • Luty 2012
    • Listopad 2011
    • Październik 2011
    • Wrzesień 2011
    • Sierpień 2011
    • Lipiec 2011
    • Maj 2011
    • Kwiecień 2011
    • Marzec 2011
    • Luty 2011
    • Styczeń 2011
    • Grudzień 2010
    • Listopad 2010
    • Październik 2010
    • Wrzesień 2010
    • Sierpień 2010
    • Lipiec 2010
    • Czerwiec 2010
    • Maj 2010
    • Kwiecień 2010
    • Marzec 2010
    • Luty 2010
    • Styczeń 2010
  • Strona główna
  • Curriculum Vitae
  • O mnie
  • Mapa strony
  • Kontakt

Kamil Brenk © 2010. All rights reserved.

Designed by FTL Wordpress Themes brought to you by Smashing Magazine.

Do góry ∧