V předchozím článku jste se mohli dočíst o využití CSS signature a přepínače stylových předpisů pro skinování stránek. V tomto článku vám předvedu, jak aplikovat skinování na obrázky, nikoli pouhou jejich záměnou, ale přímou editací jednotlivých jejich součástí na serveru.

Počítadlo ve velkém stylu

Dnešní povídání začnu konkrétním příkladem a postupně si celou problematiku zobecníme. Jako příklad nám poslouží následující počítadlo návštěvníků:

ukázka obrázku pro návštěvníka číslo 180103

Samozřejmě zde nebudu popisovat, jakým způsobem funguje. Naopak budu předpokládat, že takové počítadlo návštěvníků na svých stránkách máte, ať už nějaké supergrafické nebo nevtíravější textové. Čili předpokladem využití popisovaného řešení je, že máte počítací systém a chcete patřičné číslo stylově zobrazit ve stránce luxusně vybavené grafikou.

Náš systém personalizace bude nabízet design červený, kontrastní, modrý, šedý a oranžový (ačkoli bychom mohli generovat obrázek libovolných barev). Uživateli, kterému je příjemnější červený design, určitě nepředložíme stránku s obrázkem počítadla, jak ho vidíte na příkladu výše. Přirozeně se budeme snažit, aby takové počítadlo „zapadlo“ do designu a příliš na sebe neupozorňovalo. Analogicky uživateli, který si zvolí kontrastní zobrazení, ukážeme počítadlo černobílé s většími písmeny (aneb grafika za každou cenu).

Analýza a design

Obrázek počítadla na webu představuje objekt, jehož vstupem je:

  • číslo převzaté z počítacího systému
  • barevné schéma s jeho popisem
  • doplňkový údaj o vzdálenosti rozestupu jednotlivých čísel
  • doplňkový údaj o pozici, na které je číslo v obrázku zapsáno

Jen na okraj, obrázek počítadla bude pravděpodobně ve formátu JPEG nebo PNG. S ohledem na možnost znovupoužití programu však nepřipouštím, aby byl závislý na typu, barvě nebo velikosti. Číslo se získá, jak jsem již zmínil, z počítacího systému. (Je to jeden z předpokladů, který máme vyřešen.) Dále si pod pojmem barevné schéma představíme následující obrázek:

barevné schéma, které bylo použito pro obrázek počítadla

Popis barevného schématu může vypadat následovně:

Element Souřadnice
(levá horní)
Souřadnice
(pravá dolní)
plátno [0, 0] [350, 70]
číslo 0 [12, 70] [61, 101]
číslo 1 [70, 70] [109, 101]
číslo 2 [118, 70] [166, 101]
číslo 3 [169, 70] [216, 101]
číslo 4 [220, 70] [272, 101]
číslo 5 [11, 108] [58, 139]
číslo 6 [62, 108] [112, 139]
číslo 7 [115, 108] [164, 139]
číslo 8 [166, 108] [216, 139]
číslo 9 [219, 108] [269, 139]

Popis barevného schématu není nic jiného než soubor souřadnic, jimiž se jednoznačně vyznačí elementy, tedy plátno jako kreslící plocha a pozice desíti jednotlivých číslic. Počátek soustavy souřadnic se nachází v levém horním rohu.

představa barevného schématu s vyznačením části jeho popisu

Doplňkové údaje

Prvním doplňkovým údajem je vzdálenost jednotlivých čísel od sebe (jinými slovy rozestup, známý z CSS jako letter-spacing). Pomocí něj můžeme určit, že mezi jednotlivými číslicemi bude určitá mezera, buď v pixelech nebo, podobně jako v CSS, v relativních jednotkách EM či EX (například 0.2ex).

Druhý doplňkový údaj představuje výchozí bod. Tím, že určíme rozlohu a polohu plátna, ještě nemusí být zřejmé, kam se má dané číslo zobrazit. Na plátně kupříkladu může být vyobrazen text Stav od 21.3.2003. Definováním výchozího bodu se pak bezpečně vyhneme přepsání informace, od kdy počítadlo funguje, číslem.

Návrh a kódování

Pro prezentaci celého řešení jsem si zvolil vývojové prostředí PHP. Objektové programování v PHP, to je v současnosti často diskutované téma a objekty se skloňují ve všech pádech. Třídu, kterou si napíšeme, nazveme skin_counter. Jelikož jsem odmítl připustit, že by byla závislá na typu či velikosti obrázku, vyhneme se v objektovém návrhu deklaracím tříd jako například skin_counter_red, skin_counter_contrast a podobně.

Instanci třídy skin_counter vytvoříme takto:

$objSkinCounter = new skin_counter(‚counter_picture.png‘, $pos);

Konstruktoru rovnou předáme cestu k obrázku barevného schématu (counter_picture.png) a jeho popis (čili seznam souřadnic, který si předtím uložíme do $pos). V dalším kroku nastavíme rozestup mezi číslicemi na dva pixely:

$objSkinCounter->set_distance(2);

V případě, že tento rozestup explicitně neurčíme, výchozí hodnotou bude čtvrtina šířky jednoho z číselných elementů (právě toho, který definujeme v pořadí jako první), čili v jazyce CSS 0.25ex. Podobné chování zajistíme i v případě druhého doplňkového údaje. Výchozí pozicí pro zápis prvního čísla bude právě takový bod, který by umístil prvně definovaný číselný element vertikálně doprostřed plátna a horizontálně k pravému okraji, posunutý o dvanáctinu jeho šířky. Možná to intuitivně snáze pochopíte z pseudokódu (jinak viz metoda set_startpoint_default):

$x = zaokroulit_dolu( $platno[‚sirka‘] –
      ($prvni_element[‚sirka‘] + $prvni_element[‚sirka‘] / 12) );
$y = zaokroulit_dolu( ($platno[‚vyska‘] – $prvni_element[‚vyska‘]) / 2 );

Druhý doplňkový parametr jsme se tedy rozhodli ponechat na výchozí hodnotě. Nic nám již nebrání zavolat metodu output. Právě té parametricky předáme libovolné číslo a výsledkem jsou binární data (ve formátu shodném s barevným schématem) poslána na standardní výstup. Našemu objektu (potažmo celému skriptu) v ten moment zaniká činnost.

$objSkinCounter->output($counter_no);

Přehled vlastností a metod objektu

Jelikož přímo z kódu třídy není na první pohled patrné, které metody jsou vnitřní a které naopak tvoří interface, pro snazší orientaci uvádím jejich přehled (v případě jakýchkoli nejasností mohu poskytnout bližší vysvětlení v diskusi). Nejprve si prosviťme na vlastnosti:

  • $PATH – cesta k obrázku barevného schématu
  • $IMAGE – handler na barevné schéma (vrácený např. funkcí imagecreatefromjpeg)
  • $IMAGE_PARTS – pole handlerů na vypreparované elementy barevného schématu
  • $POSITION_DATA – data popisu barevného schématu
  • $TYPE – číslo typu výstupního obrázku (2 = JPEG, 3 = PNG)

K vlastnosti objektu ze zásady nedoporučuji přistupovat (pro zápis a čtení) přímo, nýbrž výhradně skrze veřejné metody (set* a get*). Vlastnost $POSITION_DATA je typu pole se strukturou ['MAIN'=>[0, 0, 350, 70], '0'=>[12, 70, 49, 31], ...]. Připomenu, že v run-time se můžeme na data podívat například funkcí print_r($objSkinCounter->POSITION_DATA).

Veřejné metody (public) jsou právě ty metody objektu, které jsou určeny k volání zvnějšku (tj. interface objektu). Jinými slovy, prostřednictvím veřejných metod s objektem komunikujeme, jejich znalost je proto důležitá.

  • function skin_counter($path = NULL, $position_data = NULL, $type = NULL)
    Konstruktor třídy. V pořadí lze předat cestu k barevnému schématu (viz set_path), jeho popis (viz set_position a set_position_one) a typ výstupního formátu (viz set_type, set_type_default).
  • function set_path($path)
    Nastavuje v objektu cestu k obrázku barevného schématu.
  • function set_type($type)
    Poskytuje objektu informaci o výstupním formátu (např. 2 = JPEG, 3 = PNG).
  • function set_type_default($indi = NULL)
    Za předpokladu, že dosud nebyl výstupní formát nastaven (viz set_type), nastaví se na výchozí hodnotu (v tomto případě JPEG). Pokud metodě předáme nenulový parametr, není na zmíněný předpoklad brán zřetel.
  • function set_position($key, $data)
    Zapíše do kolekce $POSITION_DATA popis právě jednoho elementu barevného schématu. První argument představuje identifikátor popisu jednoho elementu (0, 1, 2, .. a MAIN pro plátno). Jde o běžnou asociaci, kdy skrze tento identifikátor můžeme přistupovat k jeho datům. Data jsou předána jako asociativní pole, kde X,Y je pozice levého horního rohu elementu na ose X (resp. Y) a W,H je šířka (resp. výška) elementu.
  • function set_position_one($key, $x, $y, $w, $h)
    Alias set_position s jiným tvarem předání parametrů.
  • function clear_position()
    Metoda inicializuje kolekci $POSITION_DATA. Kolekce se vyprázdní, což způsobí zrušení všech předchozích nastavení metodami set_position*, set_distance* a set_startpoint*.
  • function set_distance($distance)
    V pixelech se nastaví rozestup mezi grafickými elementy barevného schématu (v tomto případě „mezera mezi čísly“).
  • function set_distance_default($indi = NULL)
    Analogicky k set_type_default nastavuje výchozí hodnotu rozestupu mezi elementy.
  • function clear_distance()
    Maže nastavení, které předtím vykonaly metody set_distance*.
  • function set_startpoint($x, $y)
    Nastaví pozici pro první element. Příklad: Nastavíme tento bod na souřadnice [200, 10] a chceme vypsat číslo 265. Na tento bod v plátně bude umístěn element pod číslem 5 (přesněji jeho levý horní roh).
  • function set_startpoint_by_rightside($x)
    S odkazem na metodu set_startpoint tato metoda nastaví souřadnici X relativně k pravému okraji plátna. Tuto vzdálenost určíme parametrem. Rozměr plátna musí být již známý. Metoda nepozicuje ve vertikálním směru, tudíž tento rozměr musí být explicitně určen (set_startpoint_by_topside), jinak má hodnotu nula.
  • function set_startpoint_by_topside($y)
    Analogicky k set_startpoint_by_rightside ve vertikálním směru.
  • function set_startpoint_default($indi = NULL)
    Nastaví pozici pro první element na výchozí bod, tj. ve vzdálenosti Q1 od pravé hrany a Q2 od horní hrany plátna. Q1 se vypočítá jako dvanáctina šířky prvně nadefinovaného elementu, Q2 je taková vzdálenost od horní hrany plátna, která by umístila prvně definovaný element do prostřed plátna.
  • function output($number, $type = NULL)
    Metoda se postará o vygenerování výsledku, který je odeslán na standardní výstup. Parametry jsou číslo (to se zobrazí) a typ výstupního formátu (nepovinný). Pokud byl formát již určen, bude mít zde předaný parametr vyšší prioritu.

Všimli jste si, že metody s prefixem „get_“ chybí? Přitom jsem psal, že z důvodu zachování vnitřní integrity dat objektu zásadně nedoporučuji přímý přístup k vlastnostem objektu! Absence metod je ovšem záměrná. Když jste dočetli až sem, předpokládám, že si také prostudujete kód třídy a nebudete mít problém si takové metody napsat dle svých potřeb.

Soukromé metody (private) jsou z vnějšího pohledu skryty a představují vnitřní implementaci objektu. Narozdíl od metod veřejných je lze nezávisle na okolí rušit, měnit názvy, upravovat počty parametrů a podobně. Privátní metody by měly pracovat pouze s vlastnostmi objektu a libovolně mohou využívat jeho metody (jak soukromé tak i veřejné). Nutno poznamenat, že v PHP až do páté verze pojmy soukromé metody či veřejné metody neexistují. Jde však o zavedené pojmy OOP. Volání soukromé metody může vypadat například takto: $this->load();.

Ve třídě skin_counter existují tyto soukromé metody:

  • function init()
  • function load()
  • function imagetype2string()
  • function info()
  • function parse_image($use_only = NULL, $use_always = NULL)
  • function make_counter(&$number_arr, &$output)
  • function error_clear()
  • function error_no()

Závěr a inspirace

Nyní už schopného programátora nepotřebujete. A když budete chtít změnit u počítadla barvu či velikost, uděláte to přirozeně ve svém oblíbeném grafickém editoru (respektive změnou popisu schématu). Věřím, že dnes nabyté poznatky snadno uplatníte, ale abyste všichni netvořili jen samá počítadla, pokusím se o pár inspirativních příkladů:

  • Obrázek semaforu
    Vytvoříte jej úpravou kódu tak, aby se elementy sázely shora dolů a ne zprava doleva. Nebo budete obrázek transponovat. Pro příkaz „volno“ byste metodě output předali řetězec GBB (green, black, black). Příklad všem naprosto jasný, že ano?
  • Smajlík na mnoho způsobů
    Odpusťte mi, pokud to nevysvětlím úplně jasně. Zde by vůbec nebylo nutné dělat zásah do kódu třídy. Vytvoříte si obrázek (čili barevné schéma) libovolné šířky. Obrázku dáte název kiss.png a jak sám název napovídá, bude tvořen smajlíky polibků, jeden vedle druhého. Řekněme, že jich bude sto. Mechanicky si vytvoříte popis, kde každý element označíte písmenem. Pro ukázku prvního polibku zavoláte metodu output s parametrem 'A'. Nabízí se kontrolní žertovná otázka – proč nemůžete jednotlivé elementy očíslovat?

Podívejte se na funkční ukázku, stáhněte si její zdrojové kódy a ostatní už je na vaší fantazii. Program jsem otestoval v PHP verzi 4.2.3. Mohu vás také ujistit, že funguje i ve verzi 4.0.5. A poslední důležitá informace – využívám knihovnu GD, která se ve výpisu funkce phpinfo() hlásí jako 1.6.2 or higher.

Žádný příspěvek v diskuzi

Odpovědět