Hlavní navigace

Podpora skriptování v grafickém formátu SVG

27. 9. 2007
Doba čtení: 11 minut

Sdílet

V dnešním článku si popíšeme vytváření animací pomocí skriptů (naprogramovaných například v JavaScriptu) a řízení vzhledu dokumentu s využitím DOM. Pomocí těchto technik lze naprogramovat i velmi složité animace, reagovat na akce prováděné uživatelem či dokonce vytvářet celé intranetové aplikace.

Obsah

1. Podpora skriptování v grafickém formátu SVG
2. Události, na které může skript reagovat
3. Načítání atributů grafických objektů
4. Změna atributů grafických objektů
5. Začlenění JavaScriptu do SVG souborů
6. Demonstrační příklad – interaktivní změna poloměru a obrysu kružnic
7. Literatura a odkazy na internetu
8. Obsah dalšího pokračování tohoto seriálu

1. Podpora skriptování v grafickém formátu SVG

Jednou z nejužitečnějších vlastností, které vektorový grafický formát SVG uživatelům i programátorům nabízí, je podpora skriptování, tj. řízení vzhledu výkresů pomocí programového kódu. Mnoho grafických formátů, které jsme si již v tomto seriálu popsali, žádné možnosti skriptování neobsahovaly. Jediným formátem, který byl založený na imperativním programovacím jazyce a ne na (jakkoli sofistikovaném, ale už principiálně omezeném) statickém popisu dat, byl PostScript – ani ten však neumožňuje naprogramovat například reakci na akce prováděné uživatelem. SVG je však primárně určen pro Web, který se stává interaktivním médiem a z tohoto důvodu je podpora skriptování v SVG nejenže logická, ale i užitečná, protože SVG posouvá od pouhého grafického a animačního vektorového formátu k ucelené platformě, nad kterou je možné vybudovat i rozsáhlejší aplikace, například mapový portál či interaktivní uživatelské rozhraní ke geografickému informačnímu systému (GIS).

Jakým způsobem je skriptování ve formátu SVG vyřešeno? Vzhledem k tomu, že je celá vektorová kresba v paměti počítače reprezentována datovou strukturou strom (tree) a obraz tohoto stromu je v serializované formě uložen ve formátu XML, lze pomocí DOM (Document Object Model) manipulovat s jednotlivými uzly stromu, podobně jako je možné manipulovat – typicky pomocí JavaScriptu – s jednotlivými elementy HTML stránky („dynamické HTML“, DHTML). V případě souborů typu SVG lze u jednotlivých uzlů měnit jejich atributy, kterými mohou být barva grafického objektu, jeho rozměry, tvar, umístění v ploše výkresu, obsah zobrazeného textu, aplikovaná lineární transformace, parametry animace atd. Ke grafickým objektům lze také zaregistrovat takzvané callback funkce (zpětně volané funkce) fungující jako ovladače událostí (event handlers) a vytvářet tak kresby, které reagují na akce prováděné uživatelem, například na stisk tlačítka myši, přejetí kurzorem myši po ploše grafického objektu, stlačení klávesy apod.

V geografických informačních systémech se například může zobrazit mapa s vykreslenými parcelami, která je vygenerovaná do formátu SVG (pravděpodobně by se jednalo o uzavřené cesty či mnohoúhelníky kombinované s texty a liniovými prvky). Po kliknutí na parcelu se (bez provedení nového dotazu na server nebo po zjednodušeném dotazu pomocí AJAX, tj. bez překreslení celé stránky) zobrazí podrobnější informace o parcele: její vlastník, výměr, slučky apod. Zařídit tuto funkcionalitu je poměrně jednoduché, protože dané informace mohou být uloženy například ve formě pole dostupného z JavaScriptového programu (samotné pole může být dynamicky vygenerováno serverem, ovšem pouze při prvním zobrazení stránky obsahující SVG). Na rozdíl od systému založeného na rastrových obrázcích a neustálých dotazech na server se jedná o mnohem rychlejší a uživatelsky příjemnější aplikaci, nehledě na možnosti posunu obrázku či změny jeho měřítka, které je prováděno pouze na straně klienta bez dalšího „obtěžování“ serveru.

Nejčastěji je pro dynamickou manipulaci s výkresy uloženými ve formátu SVG používán JavaScript (ECMAScript) a to zejména z toho důvodu, že se jedná o programovací jazyk dostupný z prakticky jakéhokoli webového prohlížeče. Existují však i rozhraní pro další programovací jazyky, například Javu, i když jejich použití je na internetu a intranetech poněkud menší, než JavaScriptu (to však neplatí pro desktopové aplikace, které mohou být napsány i v jiném programovacím jazyce než JavaScript).

2. Události, na které může skript reagovat

Program, který má komunikovat s výkresem reprezentovaným SVG souborem, většinou neběží kontinuálně, ale je založen na zpracování událostí (events). Událostí je myšlen například stisk tlačítka myši, získání fokusu grafického objektu (kurzor myši se pohybuje nad objektem), načtení výkresu, začátek animace atd. Většinou je celý běh programu řízen pouze událostmi, což znamená, že neexistuje žádná uživatelská funkce, která by běh programu centrálně řídila; tato funkce je však transparentně zabudovaná uvnitř systému událostí. V takovém případě mluvíme o programování řízeném událostmi (event programming, event driven programming). Vzhledem k tomu, že je v programu možné reagovat i na události generované časovačem (timer), je řízení animací pomocí skriptů poměrně jednoduché – při každém „tiku“ časovače se změní hodnoty proměnných řídicích animaci a na základě hodnot těchto proměnných jsou změněny pozice či jiné atributy animovaných grafických objektů.

V následující tabulce jsou vypsány ty události, se kterými se můžeme při skriptování SVG souborů nejčastěji setkat:

Událost Význam
focusin grafický objekt získal fokus (typicky zaměření kurzorem myši)
focusout grafický objekt ztratil fokus
activate grafický objekt byl aktivován klávesnicí či myší (výběrem)
click uživatel klikl na grafický objekt
mousedown uživatel stiskl tlačítko myši
mouseup uživatel pustil tlačítko myši
mouseover kurzor myši je nad objektem (uvnitř jeho hranic)
mousemove kurzor myši se pohybuje nad objektem (uvnitř jeho hranic)
mouseout kurzor myši opustil objekt
SVGLoad událost zavolaná po načtení SVG souboru
SVGAbort událost zavolaná, pokud bylo načítání souboru přerušeno
SVGError událost zavolaná při výskytu chyby
SVGResize událost zavolaná při změně velikosti celého dokumentu
SVGScroll událost zavolaná, pokud se dokumentem pohybovalo (typicky kombinací myši a klávesnice)
SVGZoom událost zavolaná, pokud se měnilo měřítko výkresu (typicky opět kombinací myši a klávesnice)

Kromě toho je možné specifikovat událost vytvořenou po „tiku“ časovače. Pro tuto činnost sice v samotném SVG neexistuje žádný specializovaný a pojmenovaný typ události, avšak v samotném JavaScriptu je k dispozici funkce setTimeout(han­dler, hodnota_zpoždění), která zaregistruje událost, jež se zavolá po zadaném časovém úseku specifikovaném v milisekundách. Použití této funkce si vysvětlíme v následující části seriálu o grafických formátech.

3. Načítání atributů grafických objektů

Při programování skriptů je mnohdy potřebné načíst jeden či více atributů příslušných k nějakému grafickému objektu či k uzlu <g>, který sdružuje více grafických objektů. Tuto činnost je většinou nutné provést ve dvou krocích. Nejdříve se musí získat reference na objekt (objekt je zde myšlen ve smyslu programovacího jazyka JavaScript), jež odpovídá některému grafickému objektu či uzlu a posléze se zavolá metoda tohoto objektu nazvaná getAttribute(„jmé­no atributu“), které se předá jméno některého atributu grafického objektu. Toto jméno přesně odpovídá jménu uvedenému ve značce objektu. Například kružnici je možné v SVG souboru vytvořit pomocí značky:

<circle id="kruznice1"
        cx="50" cy="150" r="20"
        fill="#00f"/> 

Pokud bude v proměnné nazvané gfxObject uložena reference na tuto kružnici, získá se aktuální hodnota jejího poloměru následovně:

var radius=gfxObject.getAttribute("r"); 

Použití metody getAttribute() místo pouhého zápisu gfxObject.r (tento zápis je využívaný například při změně HTML stránek pomocí DOM, jedná se třeba o výraz window.status) přináší dvě výhody – za běhu skriptu lze rozpoznat chybu ve jméně atributu a zjednodušuje se práce integrovaných vývojových prostředí (IDE), které si nemusí nikde pamatovat všechny možné atributy grafických objektů, aby rozpoznaly, zda je zápisem gfxObject.r myšlen SVG atribut grafického objektu nebo uživatelsky definovaný atribut. Jména atributů se také dají programově generovat či je mít uloženy v poli řetězců.

5701
Obrázek 1: Screenshot dnešního demonstračního příkladu pořízený ihned po jeho spuštění

4. Změna atributů grafických objektů

Změna vybraných atributů grafických objektů je stejně snadná, jako jejich přečtení, pouze se místo metody getAttribute() použije metoda setAttribute(). Prvním parametrem této metody je jméno atributu, druhým parametrem pak jeho změněná hodnota. Následuje velmi jednoduchý příklad, ve kterém se změní poloměr kružnice, která je (resp. reference na ni) v JavaScriptu uložena do proměnné gfxObject:

gfxObject.setAttribute("r", 10); 

Některé atributy grafických objektů jsou reprezentovány složitější datovou strukturou než pouhým celým či reálným číslem. V tomto případě se použije hodnota atributu zapsaná v takové formě (textové), jak je tomu i v samotném SVG souboru, například:

gfxObject.setAttribute("transform", "translate(10, 20)"); 

Na první pohled může tento zápis vypadat komplikovaně, ve skutečnosti však JavaScript obsahuje všechny potřebné nástroje, kterými je možné potřebný řetězec, tj. „translate(10, 20)“ velmi jednoduše vytvořit.

5702
Obrázek 2: Screenshot dnešního demonstračního příkladu poté, co červená kružnice získala fokus

5. Začlenění JavaScriptu do SVG souborů

JavaScript, přesněji ECMAScript, je programovací jazyk, který je určen především pro běh na straně klienta, představovaného typicky webovým prohlížečem. Tento jazyk je ovšem možné použít i přímo v SVG souborech, takže již delší dobu existují samostatné SVG prohlížeče, které JavaScript nativně podporují. Téměř samozřejmou se stává podpora JavaScriptu ve webových prohlížečích se zabudovaným zpracováním SVG (Opera, Mozilla Firefox atd.). Programový kód (skript) je možné uložit buď do samostatného souboru (většinou s koncovkou .js) nebo do samotných SVG souborů, které je potom možné distribuovat podobnou formou, jako populární Flashové animace – jak data, tak i programový kód jsou uloženy v jednom souboru, který může být zpakován GZIPem. Vzhledem k tomu, že SVG je vytvořeno na základě syntakticky velmi přísného XML, je nutné JavaScriptový kód, který samozřejmě syntaxi XML neodpovídá, ukládat do sekce CDATA párové značky <script> tak, jak je ukázáno na následujícím fragmentu kódu:

<script type="text/ecmascript"> <![CDATA[

// JavaScriptový program, který nemusí být formátován dle pravidel SVG/XML,
// ale podle standardu ECMA-262 (ISO 16262)

]]> </script> 

Ve standardu ECMA-262, tj. dokumentu, ve kterém je formálně popsaný ECMAScript/Ja­vaScript, se sice píše, že by zdrojové kódy měly být uloženy v kódování UCS-2 nebo raději UTF-16, ale v reálných aplikacích není žádný problém ani s kódováním UTF-8, které je pro většinu použití ideální (dokonce mám dojem, že zde si specifikace SVG a ECMA-262 odporují a osobně dávám přednost UTF-8 před hybridním UTF-16, který má všechny nevýhody jak různé bitové délky znaků, tak i zbytečných paměťových nároků).

5703
Obrázek 3: Screenshot dnešního demonstračního příkladu poté, co se interaktivně (pomocí myši) zvětšil poloměr tří kružnic a žlutá kružnice získala fokus

6. Demonstrační příklad – interaktivní změna poloměru a obrysu kružnic

V dnešním demonstračním příkladu, jehož zdrojový soubor si můžete stáhnout pod tímto odkazem, je ukázána reakce na události pomocí callback funkcí a také způsob začlenění JavaScriptu přímo do SVG souborů. Po spuštění tohoto příkladu se vykreslí čtyři kružnice s různými výplněmi a konstantním poloměrem nastaveným na dvacet délkových jednotek. Ke kružnicím jsou také zaregistrovány callback funkce volané v případě, že kružnice získá fokus (uživatel na ni najede kurzorem myši), ztratí fokus (kurzor myši opustí hranice objektu) a poslední callback funkce je zavolána po stisku tlačítka myši či jiného polohovacího zařízení (kliku) nad daným objektem.

Vzhledem k tomu, že callback funkce jsou pro všechny kružnice společné, musí se použít reference na objekt, u kterého událost vznikla, aby se změna atributů projevila na správném grafickém objektu. Tato reference je představována atributem objektu target proměnné evt, kterou nastavuje samotný systém při vzniku události. Při najetí kurzorem myši nad nějakou kružnici se zvýrazní její okraj změnou atributu stroke-width. Při kliku tlačítkem myši na kružnici se změní její poloměr z dvaceti na čtyřicet délkových jednotek a naopak. Poloměr kružnice je uložen v atributu r, který je jak čten metodou getAttribute(), tak i měněn pomocí metody setAttribute().

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
                     "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400"
     height="400"
     viewBox="0 0 200 200"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">

     <!-- JavaScriptový kód pro dynamickou změnu
          vlastností grafických objektů -->
    <script type="text/ecmascript"> <![CDATA[

    // funkce zavolaná po výběru grafického objektu
    function onGfxObjectClick(event)
    {
        var gfxObject=event.target;
        var radius=gfxObject.getAttribute("r");
        if (radius==40)
            gfxObject.setAttribute("r", 20)
        else
            gfxObject.setAttribute("r", 40)
    }

    // funkce zavolaná po získání fokusu grafického objektu
    function onFocusIn(event) {
        var gfxObject=event.target;
        gfxObject.setAttribute("stroke", "#000")
        gfxObject.setAttribute("stroke-width", 5)
    }

    // funkce zavolaná po ztracení fokusu grafického objektu
    function onFocusOut(event) {
        var gfxObject=event.target;
        gfxObject.setAttribute("stroke-width", 0)
    }
    ]]> </script>

    <!-- první grafický objekt -->
    <circle onclick="onGfxObjectClick(evt)"
            onfocusin="onFocusIn(evt)"
            onfocusout="onFocusOut(evt)"
            cx="50" cy="50" r="20"
            fill="#f00"/>

    <!-- druhý grafický objekt -->
    <circle onclick="onGfxObjectClick(evt)"
            onfocusin="onFocusIn(evt)"
            onfocusout="onFocusOut(evt)"
            cx="150" cy="50" r="20"
            fill="#0f0"/>

    <!-- třetí grafický objekt -->
    <circle onclick="onGfxObjectClick(evt)"
            onfocusin="onFocusIn(evt)"
            onfocusout="onFocusOut(evt)"
            cx="50" cy="150" r="20"
            fill="#00f"/>

    <!-- čtvrtý grafický objekt -->
    <circle onclick="onGfxObjectClick(evt)"
            onfocusin="onFocusIn(evt)"
            onfocusout="onFocusOut(evt)"
            cx="150" cy="150" r="20"
            fill="#ff0"/>
</svg> 

5704
Obrázek 4: Všechny čtyři kružnice mají poloměr nastavený na 40 jednotek, žádná nemá fokus

ict ve školství 24

7. Literatura a odkazy na internetu

  1. ECMAScript Language Specification,
    Standard ECMA-262, 3rd Edition, December 1999
  2. Self: The Power of Simplicity,
    Ungar, David, and Smith, Randall B.
    OOPSLA '87 Conference Proceedings, pp. 227–241, Orlando, FL, October 1987
  3. Scalable Vector Graphics (SVG) 1.0 Specification,
    (W3C Recommendation)
  4. Scalable Vector Graphics (SVG),
    XML Graphics for the Web:
    http://www.w3­.org/Graphics/SVG//
  5. Cascading Style Sheets, level 2,
    B. Bos, H. W. Lie, C. Lilley, I. Jacobs, 12 May 1998.
  6. Document Object Model (DOM) Level 1 Specification,
    V. Apparao, S. Byrne, M. Champion, S. Isaacs, I. Jacobs, A. Le Hors, G. Nicol, J. Robie, R. Sutor, C. Wilson, L. Wood, editors, 1 October 1998.
  7. Document Object Model (DOM) Level 2 Core Specification,
    A. Le Hors, P. Le Hégaret, L. Wood, G. Nicol, J. Robie, M. Champion, S. Byrne, editors, 13 November, 2000.
  8. SMIL Animation,
    P. Schmitz, A. Cohen, editors, 31-July-2000.
  9. XML Linking Language (XLink),
    S. DeRose, E. Maler, D. Orchard, editors, 20 December 2000.
  10. Extensible Markup Language (XML) 1.0 (Second Edition),
    T. Bray, J. Paoli, C.M. Sperberg-McQueen, E. Maler, editors, 6 October 2000.
  11. XML Base,
    J. Marsh , editor, 20 December 2000.
  12. Namespaces in XML,
    T. Bray, D. Hollander, A. Layman, editors, 14 January 1999.
  13. Batik SVG Toolkit,
    http://xmlgrap­hics.apache.or­g/batik/
  14. SVG-Wiki,
    http://wiki.svg­.org/Main_Page
  15. Wikipedia EN: Scalable Vector Graphics,
    http://en.wiki­pedia.org/wiki/Sca­lable_Vector_Grap­hics
  16. Wikipedia CZ: Scalable Vector Graphics,
    http://cs.wiki­pedia.org/wiki/Sca­lable_Vector_Grap­hics
  17. Kurz SVG – tvorba vektorové grafiky v XML,
    http://interval­.cz/serialy/kurz-svg-tvorba-vektorove-grafiky-v-xml/

8. Obsah dalšího pokračování tohoto seriálu

V následující části seriálu o grafických formátech a metaformátech si použití programových skriptů vytvářených (především) v JavaScriptu ukážeme na několika praktických příkladech. Zaměříme se zejména na tvorbu jednodušších animací, které bude možné používat i v prohlížečích, které korektně neimplementují animační elementy SVG. Také si ukážeme reakci na další události, jež mohou vzniknout jak činností uživatele, tak i v samotném SVG prohlížeči či operačním systému.

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.