JAK na animované menu

V dnešním díle se podíváme na jednu z možných implementací animovaného menu nasazeného na stránkách babybox.cz. Byť je tato implementace nasazená v provozu, patří právě mezi ty, které osobně nedoporučuji. Nicméně zde bylo na výběr něco v javascriptu, co by zvládl i indexovací robot, nebo nějaký flash zmetek, volba byla tedy jasná.

18.3.2011 00:00 | Ondřej Tůma | přečteno 11800×

Zadání

Zadavatel společně s grafickým studiem vymysleli vizuálně hezké, nicméně jak už to v praxi bývá, na webu obtížně realizovatelné animované menu. Celý systém funguje tak, že je nejprve nutno kliknout na jednu z položek v hlavního menu, ta se v několika krocích rozbalí do submenu a až následně jednotlivé části jsou odkazy na konkrétní stránky.

S lítostí musím konstatovat, že návštěvníci stránek bez podpory javascriptu mají prostě smůlu. Roboti jsou na tom o něco lépe díky výskytu obrázkové mapy, která sice není svázána s obrázkem, ale robotům to by ale nemělo činit potíže.

Optimalizace

Animované menu se v javascriptu implementuje docela jednoduše. Resp. prostě se v časovém intervalu mění obrázek, a to vytváří dojem animace. Problém ale vzniká v momentě, když těch obrázků je mnoho a nejsou dopředu načteny. Na strojích s pomalejším připojení k internetu a nakonec, ne jen tam, mohou vznikat nepříjemné prodlevy v načítání jednotlivých obrázků animace. Navíc se browser zbytečně ptá serveru vícekrát, v našem případě až 17krát.



Toho lze obejít jednoduchým trikem a to tak, že všechny obrázky se spojí do jednoho velkého. Pomocí stylů se pak zobrazí jen první snímek, část obrázku a při animaci se posouvá tento obrázek. V praxi to znamená že hned na počátku se načte jeden větší obrázek a při spuštění animace se již pracuje s tímto načteným obrázkem. Ve výsledku, ten jeden velký obrázek není přesně n-krát větší, protože většina obrázkových formátů obaluje samotná data ještě různými metadaty a ty jsou jen jedny. Navíc, v případě komprese většího množství dat je vyšší šance, že dojde k optimalizaci. Nakonec si ke každému obrázku můžeme připočíst obal v podobě tcp/ip, resp. http požadavku, a vedle „spolehlivosti” získáme ještě menší nároky na datový objem.

Samotná implementace

Jednotlivé animované části jsou implementované pomocí JAK třídy. Ta na vstupu dostane jenom informace o počtu kroků animace a název, ze kterého pak vygeneruje názvy ostatních html prvků. Samotný kód je docela jednoduchý, je to malinká funkce, která se stará o animaci a pak pár mechanismů, které spojují obrázkovou mapu s animovanou položkou menu.

Třída obsahuje navíc i jednu metodu, která není navěšená na žádnou událost, resp. toto navázání je zakomentované. Tuto metodu jsem použil pro snazší tvorbu obrázkové mapy.

Hurá kód

Do těla článku záměrně vkládám jen některé kousky kódu. Celý příklad je přiložen k článku jako zip archív.

<div id="menuA" class="item">
            <img id="menuA-img" src="http://zeropage.cz/menu_fake.png" alt=""/>
            <map name="menuA-map">
                <area id="menuA-A" shape="polygon" coords="9,93, 57,11, 74,7, 85,23, 65,61, 21,89, 9,93"
alt="Archív aktualit" href="http://zeropage.cz/?p=aktuality-archiv"/>
                <area id="menuA-B" shape="polygon" coords="21,89, 105,40, 118,48, 121,65, 80,90, 28,90, 21,89"
alt="Poslední měsíc" href="http://zeropage.cz/?p=aktuality-mesic"/>
                <area id="menuA-C" shape="polygon" coords="28,90, 127,90, 137,108, 128,123, 84,125, 30,95, 28,90"
alt="Poslední týden"href="http://zeropage.cz/?p=aktuality-tyden"/>
                <area id="menuA-D" shape="polygon" coords="30,95, 121,147, 122,166, 106,177, 67,157, 30,95"
alt="Novinka" href="http://zeropage.cz/?p=novinka"/>
            </map>
        </div>

V html je nutné vytvořit některé prvky jednotlivých animovaných položek. Pokusím se je ve zkratce popsat. Fake obrázek je průhledný obrázek sloužící k propojení s obrázkovou mapou. Toto propojení nastane až po rozbalení, aby se na odkazy nedalo kliknout pokud položka není rozbalená. [Právě toto by šlo upravit tak, aby mapa byla odebrána až javascriptem, pak by i návštěvníci bez javascriptu mohli klikat na odkazy, jen by je neviděli :(] Obrázková mapa definuje jak polygony jednotlivých položek animovaného menu, tak odkazy těchto položek. Celé je to uzavřené v divu. Ten má id, které je společným základem všech html prvků patřící právě k jedné skupině. To nám usnadní propojení s javascriptem.

Javascrtiptová třída MenuItem

Konstruktor třídy MenuItem, tedy jednoho animovaného submenu, obsahuje mimo jiných i tyto řádky:

    this.animateInterval = null;
    this.timeoutedClose = null;
    this.listeners = new Array();

    this._animate = this._animate.bind(this);
    this._timedClose = this._timedClose.bind(this);

    JAK.Events.addListener(this.dom, 'click', this, '_open');
    //JAK.Events.addListener(this.dom, 'mousemove', this, '_move');
    JAK.Events.addListener(this.dom, 'mouseout', this, '_close');

V kódu je navěšení metod na události animovaného menu. A hned pole listeners napovídá že jich bude víc než jen click a mouseout.

MenuItem.prototype._open = function(e, elm){
    pos = JAK.DOM.getPortBoxPosition(elm);
    elementX = Math.round(e.clientX - pos.left);
    elementY = Math.round(e.clientY - pos.top);
    if (elementX > 5 && elementX < 140 && elementY > 90 && elementY < 125){
        this.vertex = 1;
        if (!this.animateInterval)
            this.animateInterval = setInterval(this._animate, 50);
    } else {
        console.log('X: '+elementX);
        console.log('Y: '+elementY);
    }
}

Metoda _open, jak je z názvu a události na které je navěšená patrno, se stará o rozbalení jednoho menu. Protože ale zabalené menu je daleko menší než rozbalené, hlídá si tato metoda rozsah souřadnic, kde bylo kliknuto a tedy ohraničuje akci rozbalení jen na vymezenou oblast.

MenuItem.prototype._animate = function(){
    if (this.vertex == 1){   // otevirame
        this.dom.style.zIndex = 2;
        if (this.img == this.steps) {
            clearInterval(this.animateInterval);
            this.animateInterval = null;
            this._openAtEnd();
        } else
            this.img += 1;    
    } else {            // zavirame
        this._closeAtStart();
        if (this.img == 0){
            clearInterval(this.animateInterval);
            this.animateInterval = null;
            this.dom.style.zIndex = 1;
        } else
            this.img -= 1;
    }

    var s = ""+(this.img*-143) + "px 0px";
    this.dom.style.backgroundPosition = s;
}

Stěžejní metodou naší třídy je _animate. Ta se stará právě o požadovanou animaci. Jak je z kódu vidět, metoda detekuje směr nastavený metodami _open a_close a dle směru posune obrázek v pozadí. Tím vytváří efekt animace. Pokud byla animace dokončena, vymaže se naplánovaná akce - spuštění metody _animate. Navíc je v příhodném okamžiku puštěná další metoda _closeAtStart nebo _openAtEnd.

Tyto metody mají za úkol navázat, resp. odebrat obrázkovou mapu a příslušné metody s jednotlivými položkami rozbaleného menu. Protože při akci mouseout chceme, aby byl vybraný prvek označen.

MenuItem.prototype._overItem = function(e, elm){
    if (this.timeoutedClose != null){
        clearTimeout(this.timeoutedClose);
        this.timeoutedClose = null;
    }

    if (elm == this.domA)
        this.img = this.steps + 4;
    if (elm == this.domB)
        this.img = this.steps + 3;
    if (elm == this.domC)
        this.img = this.steps + 2;
    if (elm == this.domD)
        this.img = this.steps + 1;

    var s = ""+(this.img*-143) + "px 0px";
    this.dom.style.backgroundPosition = s;
}

Metoda _overItem provede změnu podobnou jako provádí metoda _animate. Metoda _over resetuje stav rozbaleného menu bez vybraného prvku. To proto, aby při zavírání nedošlo k nechtěným efektům.

Zajímavá je ještě metoda _close, ta ve skutečnosti menu nazavře, ale jen ono zavření načasuje. To proto mazání načasovaného zavření v metodách _over a _overItem.

Závěrem

Výše popsaný a k článku přiložený kód rozhodně není dokonalý a ještě by ho šlo o mnohé vylepšit. Je ale funkční a zadavatel získal přesně to co vyžadoval. Laskavého čtenáře tedy odkážu právě na přiložený příklad a jeho hlubší studii s tím, že kód dávám k volnému šíření a použití zdarma pod BSD licencí. Příště si ukážeme JAK se dá udělat vlastní scrollovací prvek.
Online verze článku: http://www.linuxsoft.cz/article.php?id_article=1816