3. prosince 2008

Flash (Flex) nebo Silverlight?

Jak asi dopadne souboj vzájemně si konkurujících technologií Adobe Flash a Microsoft Silverlight? Stane se časem jedna z nich dominantní? Má smysl se pro jistotu naučit obě, abychom byli připraveni, až jedna z nich náhle odejde ze scény?

Zachovejme paniku :o)

Jakmile se člověk oddá futuristickým úvahám, ocitne se vždy na tenkém ledě. Navíc, pokud svou věšteckou kouli máte také zahrabanou někde v koutě skříně, protože jste se nemohli při jejím používání zbavit dojmu, že:

a) se vás snaží odsunout do role pasivního diváka,
b) je asi nějak porouchaná, neb jste místy nevěděli, na co se vlastně díváte,

tak jste vlastně odkázaní jen na své vlastní znalosti a svou vlastní fantazii. Přiznejme si, že obojího není nikdy dost, tedy pokud se náhodou nejmenujete Leonardo nebo Jules.

Přesto mi to nedá a jisté futuristické úvahy se v otázce souboje Flashe a Silverlightu dopustím. I když pro mne osobně to bude vlastně vzpomínání, tak jakápak vlastně futuristika (myšleno turistika do budoucnosti), že?

Souboj vyvíjejících se technologií, které různým způsobem řeší v podstatě tytéž potřeby a problémy, jsem totiž už jednou prožil, a to od začátku až do konce. Bylo to v oblasti vývoje CAD systémů. "Kreslit" v CADu jsem začal už v roce 1989 a shodou okolností v systému AutoCAD. Prakticky už od začátku jsem AutoCAD používal nejen jako náhradu klasického rýsovacího prkna (2D), ale začal jsem v něm i modelovat ve 3D (verze 11 měla už primitivní objemový modelář, ale své úplně první 3D zakázky jsem byl s to řešit, světe div se, již ve verzi 10, která měla alespoň možnost zadat z-tovou souřadnici a uměla zobrazit perspektivu). Proč se o tom zmiňuji?

Musím totiž začít trochu zeširoka. Na počátku 90-tých let byla situace taková, že existovalo několik velmi drahých softwarových balíků pro návrh ve 3D. Ty byly pro mne nedostupné. Začínal jsem totiž zrovna na volné noze a chtěl jsem se vyhnout miliónovému úvěru (jo jo, tenkrát byl milión opravdu milión). Přitom 3D jsem pro sebe viděl jako možnost se úspěšně prosadit na trhu a hlavně "směr". Proto jsem se musel spokojit s relativně levným řešení ve "střední" třídě a naučit se využívat všechny možnosti na maximum. To, že jsem "se dal" zrovna na AutoCAD, byla více méně náhoda. Mohl jsem zrovna tak začít s CADKey či MicroStation.

Tyto tři "střední" CAD systémy si tehdy konkurovaly podobně jako si dnes konkuruje Flash se Silverlight. Stejně tak totiž mířily do oblasti, která byla zatím vyhrazená jen opravdu masivním a drahým řešením, anebo nebyla řešená vůbec. Prakticky celá 90-tá léta byla ve střední třídě CADů ve znamení objevování a přidávání dalších a dalších funkčností, až ...

... až se vývoj posunul k hranici, kdy první derivace notoricky známé S-křivky inovačního procesu začíná klesat k nule ... ehm ... kdy se tempo vývoje opět začíná zpomalovat na původní úroveň, protože "území" je dobyto a zmapováno, silnice postaveny, telefonní linky nataženy a už se jen sází kytky do zahrádek.

Zažil jsem v době bouřlivého rozvoje řadu vášnivých diskusí na téma, který CAD systém je lepší. Mně ty debaty ale vždy přišly trochu zbytečné. Jak už jsem říkal: musel jsem se naučit využívat AutoCAD na maximum a díky tomu jsem v něm pak dokázal dělat věci, které jiný nedokázal dělat ani s o třídu vyšším softwarem, anebo jsem byl schopný se mu alespoň vyrovnat v rychlosti i výsledku. AutoCADu jsem prostě dokonale rozuměl, znal jeho světlé i tmavé kouty, "cítil" ho a uměl (a stále umím) v něm myslet. Musel jsem se také pořád učit, protože AutoCAD přinášel s každou další verzi řadu nových možností a dílčích změn. Abych přešel na jiný CAD systém, musel by mi nabídnout něco opravdu trvale výjimečného, abych měl opravdu reálný důvod začít úplně od začátku, sžít se odlišnou filozofií a pak se dostat vlastně zase do stejné situace, kde jsem byl před tím s AutoCADem. Nic takového mi žádný systém nikdy nenabídl. A to vždy rozhodlo.

Je to myslím podstatné i v případě Flashe a Silverligtu. Oba systémy jsou už ve fázi velmi bouřlivého vývoje. Obě technologie cílí na řešení v podstatě stejného okruhu technických problémů a v pozadí obou stojí opravdu chytří lidé i silní producenti. A tak Flash i Silverlight "dělají" a budou "dělat" v podstatě totéž, jen "různým" způsobem a pomocí jinak postavených editačních nástrojů. Pokud se budou obě technologie vyvíjet dál (a to samotné se opravdu předpovědět nedá), obě se časem stanou velmi propracované a tedy náročné na zvládnutí. Vyžadují a budou vyžadovat nestálé učení se. A co je velmi důležité, obě technologie se budou muset naučit velmi dobře komunikovat se svým okolím a také se sebou navzájem - přestanou tedy být oddělenými světy.

Brzo se tedy dostaneme do situace, kdy jakékoli zadání budeme schopni řešit Flashem stejně dobře a efektivně jako Silverlightem, nebo i jejich libovolnou kombinací. Obě technologie na to potenciál mají, očividně tím směrem i míří, takže myslím jde jen o to, jestli to jejich producenti sami nezabalí nebo jinak nepokazí.

Marketing sem a mýty tam. Prostě problém není v tom, co která technologie bude umět a druhá ne. Spíš rozhodne, co v té či oné technologii budeme sami schopní udělat a do jaké hloubky jí vlastně budeme rozumět. Zákazník s příslušnými požadavky se pak vždy najde. A totéž platí i o spolupracovnících z druhé strany barikády.

Tak pravil ten, jenž bude muset jít za chvíli na oběd ... :o)

18. září 2008

Když vázne komunikace mezi flash animacemi při více otevřených oknech prohlížeče

Komunikace mezi více Flash animacemi, které jsou umístěny na jedné HTML stránce se dá velmi elegantně řešit pomocí třídy Action Sriptu LocalConnection. Je však nutné ošetřit případ, kdy bude stejná HTML stránka otevřená ve více oknech. Jedno z řešení tohoto problému popisuji níže.

Jak se komunikace mezi SWF v jedné HTML stránce vlastně naprogramuje?

Představte si, že máte na stejné HTML stránce vložené dvě Flash animace (movie_1.swf a movie_2.swf) a chcete je navzájem synchronizovat, tzn. že animace z movie_2.swf se má spustit až v okamžiku, kdy animace v movie_1.swf dospěje do určité fáze - dejme tomu až přehrávání animace doběhne k snímku č.79.

Pomocí třídy Action Scriptu LocalConnection to vyřešíte tak, že do movie_1.swf na snímek č.79 vložíte následující kód:

var LC_12:LocalConnection = new LocalConnection();
LC_12.connect("CN_12");
LC_12.send("CN_12","FN_receiver_12");

a do movie_2.swf na první snímek tento kód:

stop();
var LC_12:LocalConnection = new LocalConnection();
LC_12.connect("CN_12");
LC_12.FN_receiver_12 = function()
{
_root.gotoAndPlay(2);
};

Přepodkládám přitom, že obě animace probíhají na hlavní časové ose, že animace v movie_1.swf se spustila a že animace v movie_2.swf začíná až na snímku č.2. Jiný způsob užití stejného postupu najdete např. v článku Komunikace mezi dvěma SWF soubory.

Je to jednoduché a jasné - prostě skvělé pro použití. Zádrhel je ovšem v tom, že když tutéž HTML stránku se zmíněnými Flash animacemi otevřete ve více oknech, budou všechny instance LocalConnection shodné napříč všemi okny. To, že se budou jmenovat stejně nevadí. Vadí to, že volají i naslouchají na stejné "telefonní lince", jejíž "číslo" jsem v AS nastavil natvrdo na "CN_12" - okno, neokno. Tím ovšem příjímač ve všech otevřených movie_2.swf totálně zmatu.

Co s tím?

Chtělo by to prostě pro každé okno unikátní "telefonní číslo". Pokud HTML stránku generujete dynamicky, nebo se můžete spolehnout na pomoc Java Scriptu, máte vyhráno. Stačí, když do tagu OBJECT, kterým vkládáte každou z obou zméněných Flash animací do HTML stránky, připojíte za název SWF souboru proměnnou, jejícž hodnotu nastavíte pro obě animace v téže HTML stránce sice shodně, ale okno od okna unikátně.

Proměnná se může jmenovat např. "cnprefix" a v HTML stránce pak bude na místě vložení SWF tento kód:

movie_1.swf?cnprefix=abcd

a

movie_2.swf?cnprefix=abcd

Pozor! Hodnota proměnné "cnprefix" musí být v jedné HTML stránce stejná, ovšem různá v různých oknech s toutéž stránkou. Asi nejbezpečnější je vkládat hodnotu časového razítka sečtenou s nějakým náhodně vygenerovanýcm řetězcem, což zaručí neopakovatelnost. Takže v jednom okně najdneme např.:

movie_1.swf?cnprefix=abcd2368358479
movie_2.swf?cnprefix=abcd2368358479

v druhém

movie_1.swf?cnprefix=xac2368358479
movie_2.swf?cnprefix=xac2368358479

(i když, jak to mohl vlastně uživatel stihnout, že) a v třetím

movie_1.swf?cnprefix=efg2368358521
movie_2.swf?cnprefix=efg2368358521

V obou Flash animacích se pak trochu změní kód Action Scriptu. V movie_1.swf bude:

var LC_12:LocalConnection = new LocalConnection();
LC_12.connect(_root.cnprefix + "CN_12");
LC_12.send(_root.cnprefix + "CN_12","FN_receiver_12");

a v movie_2.swf:

stop();
var LC_12:LocalConnection = new LocalConnection();
LC_12.connect(_root.cnprefix + "CN_12");
LC_12.FN_receiver_12 = function()
{
_root.gotoAndPlay(2);
};

Proč pro hodnotu "telefonnícho čísla" používám součet řetězců _root.cnprefix + "CN_12" a ne přímo hodnotu _root.cnprefix? Je to proto, že v rámci obou animací mohu chtít pro jiné účely vytvářet další instance LocalConnection a každá z nich musí používat unikátní "telefonní číslo".

Z důvodů přehlednosti se mi osvědčil postup, kdy jednotlivé komunikující dvojice instancí LocalConnection používají "telefonní čísla" jichž hodnota vyjadřuje, odkud kam se volá, tzn. např.: CN_12, CN_21, CN_21_A, CN_21_B atd. Tím mám zajištěnou unikátnost "telefonních linek" v rámci jedné HTML stránky a připojení hodnoty "cnprefix" jako předpony (proto tedy prefix) k všem "telefonním číslům" mi zas zaručuje unikátnost "linek" v jednotlivých oknech prohlížeče navzájem.

12. září 2008

Jak jednoduše převést jakékoli reálné číslo na celé, které bude ležet v povoleném intervalu hodnot

Pokud ve Flash animaci potřebujete ošetřit vstup číslené hodnoty tak, abyste měli k dispozici číslo, které bude vždy celé a bude spadat do souvislého intervalu ostře omezeného hodnotami "n1" a "n2" (tzn. že tyto vymezující hodnoty patří do množiny povolených čísel), nemusíte v Action Scriptu psát žádnou složitou testovací proceduru.

Vystačíte si s jedním řádkem kódu, který jakékoli reálné číslo "x" převede na číslo celé "n" a pokud je toto zaokrouhlené číslo vně intervalu "n1" až "n2", přiřadí mu nejbližší krajní hodnotu:

n = Math.min(Math.max(n1, Math.floor(x)), n2);

Místo Math.floor(x) můžete samosebou použít Math.ceil(x) nebo Math.round(x), podle toho, jaký druh systematické chyby na vstupu předpokládáte.

Navíc zápis Math.min(Math.max(n1, n), n2); má sám o sobě elegantní tvar, který se nejen dobře pamatuje a zapisuje, ale má i jasnou vypovídací hodnotu.

2. září 2008

Pořadí změny rozměrů instance movieklipu a nastavení její rotace

Taková banalita a potrápila mne skoro hodinu, než jsem si uvědomil v čem je problém. Ale to bude tím, že jsem závadu hledal nejprve úplně jinde (píšu / psal jsem zrovna kód pro složitou aplikaci, takže než jsem se přes všechny její vrstvy dopracoval k jádru ... :o).

Oč jde?

Představte si, že máte na scéně Flash animace umístěnou instanci nebo ještě lépe více instancí movie klipu, kterým jednou za čas (v rámci resetu nastavení Flash animace) nastavujete rozměry pomocí vlastností ._width a ._height (v ActionScriptu samozřejmě). Mezi jednotlivými resety těmi instacemi po scéně různě pohybujete a někdy je i otáčíte pomocí vlastnosti ._rotation (opět v ActionScriptu). Čas od času se stane, že po resetu jsou některé instance menší, než by měly.

Možná už tušíte, kde je problém. Ano, chyba se projevuje tehdy, když je natočení "postižené" instance před resetem jiné než 0° nebo 180°.

Příčina takového divného chování je potom v pořadi, v jakém při reset nastavujete jednotlivým instancím rozměry (._width, ._height) a natočení (._rotation). Pro zjednodušení si představte, že instance jsou obdélníkové a při ._rotation=0° mají hrany rovnoběžné se scénou. Pokud budou instance před resetu natočené jinak, a reset provedete v pořadí:

instance_mc._width = sirka;
instance_mc._height = vyska;
instance_mc._rotation = 0;

bude jejich velikost (šířka a výška) nastavená tak, aby se natočené vešly do ohraničujícího obdélníku o velikosti sirka x vyska, taže po nasledném srovnání budou menší a zdeformované.

Někdy je taková věc účelná, ale pokud chcete, aby rozměry v základní poloze "seděly", musíte instance nedříve otočit do základní polohy a pak teprve nastavovat rozměry:

instance_mc._rotation = 0;
instance_mc._width = sirka;
instance_mc._height = vyska;

Pravda. Pokud rozměry instancí během pohybování s nimi neměníte a pouze je otáčíte (jak tomu bylo v mém případě), je opětovné nastavování rozměrů vlastně zbytečné (když já se ale držím zásady, že když reset, tak všeho :o). Přesto tak musíte postupovat vlastně kdykoli, kdy rozměry instance na scéně měníte a instance je v době změny rozměrů natočená jinak, než na 0° nebo 180°:

uhelNatoceni = instance_mc._rotation;
instance_mc._rotation = 0;
instance_mc._width = sirka;
instance_mc._height = vyska;
instance_mc._rotation = uhelNatoceni;

resp.:

instance_mc._rotation = 0;
instance_mc._width = sirka;
instance_mc._height = vyska;
instance_mc._rotation = novyUhelNatoceni;

Jak říkám, je to banalita :o)

5. srpna 2008

Jak zmenšit velikost FLA souboru

Na tento problém jste jistě už narazili, pokud pro nový projekt použijete jiný FLA soubor jako šablonu, protože chcete například použít některé jeho prvky a ušetřit si tak práci. Anebo jen děláte novou verzi aplikace, kde se řada věcí musí odstranit a nahradit jinými.

Je to jednoduché. Prostě překopírujete původní FLA soubor do nového adresáře, otevřete jej a smažete z knihovny vše, co v novém projektu potřebovat nebudete. Po opětovném uložení tohoto nového FLA však zjistíte, že jeho velikost na disku se nezmenšila, jak byste čekali. Je to nepříjemné zejména u FLA souborů dosahujících velikosti desítek MB.

Co s tím?

Řešení je velmi jednoduché. Stačí naklonovaný FLA soubor po vymazání nepotřebných prvků uložit pod novým jménem přímo z editoru Flashe (volba "save as").

28. července 2008

Programování postupných procesů ve Flashi

Při vytváření složitější Flash animace často narazíte na problém, jak šikovně naprogramovat jednotlivé na sebe postupně navazující fáze animace, kdy pro přechod z jedné fáze do druhé je nutné, aby byla splněna pokaždé jiná podmínka. Použití konstrukce if - else if - else je přitom problematické, protože do ní musíte zahrnout nejen podmínky nutné pro provedení určité části kódu, ale také podmínky, které vylučují provedení dané části částí kódu po nabytí platnosti jiných podmínek.

Je nutné opravdu dobře promyslet všechny možné kombinace podmínek a přesně je vymezit. Musíte se přitom zabývat i ošetřením kombinací, které Vás vlastně ani nezajímají. Ve výsledku pak často obdržíte velmi nepřehledný a nepružný kód, který nám znesnadní práci a budoucí úpravy.

Ano, je pravda, že následující kód je jasný, elegantní a přehledný:

_root.onEnterFrame = function()
{
// stanoveni aktualni hodnoty promenne cas
if (cas > 5)
{
// kod pro casovy interval (5,10>
}
else if (cas > 10)
{
// kod pro casovy interval (10,15>
}
else if (cas > 15)
{
// kod pro casovy interval (15,20>
}
else if (cas > 20)
{
// nic ... uz se nema provadet zadny kod
}
else
{
// kod pro casovy interval <0,5>
}
};

Nyní si ale představte, že po dosažení času 5s chcete nějaké akce provést jen jednorázově a po té až do 10s provádět jiné příkazy. Dále, že po dosažení 10s musíte otestovat, jestli již doběhla nějaká jiná část animace až do konce a teprve pak provést opět jednorázově nějaké příkazy a až do 15s provádět pak úplně jiné a teprve pak vás bude zajímat, jestli čas už přesáhl 15s atd. Vznikne Vám pak kód, z něhož se kamsi vytratila elegance a hlavně "jednoznačnost na první pohled":

_root.onEnterFrame = function()
{
// stanoveni aktualni hodnoty promenne cas
if (cas > 5)
{
if (!cas5)
{
// kod jednorazovy po dosazeni 5s
cas5 = true;
}
else
{
// kod pro casovy interval (5,10>
}
}
else if (cas > 10)
{
if (dobehlaAnimacePro10)
{
if (!cas10)
{
// kod jednorazovy po dosazeni 10s a splneni podminky dobehnuti jine animace
cas10 = true;
}
else
{
if (cas > 15)
{
// kod pro casovy interval (15,20>
}
else if (cas > 20)
{
// nic ... uz se nema provadet zadny kod
}
else
{
// kod pro casovy interval (10,15>
}
}
}
}
else
{
// kod pro casovy interval <0,5>
}
};

Hrůza, že?

Když se nad tím, co provádí uvedený rozhodovací strom zamyslíte, uvědomíte si, že se jedná o postupné fáze nějakého procesu, kde se jako postačující rozlišovací údaj pro provedení té které fáze dá použít pouze název dané fáze. Ten si můžete uložit do pomocné proměnné s příznačným jménem faze a pak už je snadné napsat pouze jednoúrovňový řetězec podmínek if - else if, kde se bude testovat pouze hodnota proměnné faze a nic jiného. Pro ohraničení jednotlivých částí kódu lze stejně tak použít i konstrukci switch - case - ta se mně osobně líbí více:

faze = "_0-5_";
_root.onEnterFrame = function()
{
// stanoveni aktualni hodnoty promenne cas
switch (faze)
{
case "_0-5_" :
// kod pro casovy interval <0,5>
if (cas > 5)
{
faze = "_5_";
}
break;
case "_5_" :
// kod jednorazovy po dosazeni 5s
faze = "_5-10_";
break;
case "_5-10_" :
// kod pro casovy interval (5,10>
if (cas > 10)
{
faze = "_10.1_";
}
break;
case "_10.1_" :
if (dobehlaAnimacePro10)
{
faze = "_10.2_";
}
break;
case "_10.2_" :
// kod jednorazovy po dosazeni 10s a splneni podminky dobehnuti jine animace
faze = "_10-15_";
break;
case "_10-15_" :
// kod pro casovy interval (10,15>
if (cas > 15)
{
faze = "_15-20_";
}
break;
case "_15-20_" :
// kod pro casovy interval (15,20>
if (cas > 20)
{
faze = "_20_";
}
break;
case "_20_" :
// nic ... uz se nema provadet zadny kod
// tuto vetev case vlastne uz ani psat nemusime
break;
}
};

Takovýto zápis má několik výhod. Pokud si pojmenujete jednotlivé fáze dostatečně výstižně, bude se Vám v kódu lehčeji orientovat. Také již nemusíte řešit ošetřování všech možných kombinací ohraničujících podmínek. Zde řešíte už jen to, co v dané fázi přichází v úvahu a nic víc. Můžete tak snadno přidávat či ubírat další fáze, a je jedno jestli na začátku, konci či přímo v těle stromu procesu. Můžete také některé fáze v rámci ladění programu snadno „přemostit“, a to pouhým přepsáním resp. připojením řádku pro změnu obsahu proměnné faze s odpovídající hodnotou. Můžete si na různých místech procesu vytvořit speciální fáze, které pak můžete použít jako vstupní body při skoku doprostřed procesu atd.

Princip tohoto řešení je, že použijete pomocnou proměnnou faze, přičemž pouze její obsah a nic jiného určuje, co se má v danou chvíli v rámci probíhajícího postupného procesu provádět a co již ne nebo ještě ne. Tím si často spletitý mnohoúrovňový rozhodovací strom abstrahujete do dvou jednoduše pojmutelných logických vrstev. V první vrstvě se pouze na základě obsahu proměnné faze rozhodne, co se bude provádět a teprve v druhé vrstvě, uvnitř každé části procesu se řeší, kdy se má hodnota proměnné faze změnit a jak.

Detekce vyběhnutí kursoru myši ze Stage Flash Animace v AS 2

(doplněno)

Řešil jsem problém, jak v ActionScriptu 2.0 detekovat událost, kdy myš opustí Stage, tzn. že uživatel přejede s kursorem mimo oblast Flash animace někam do okolní html stránky. Chtěl jsem se obejít bez programování nějakých pomocných funkčností mimo vlastní Flash animaci, tedy bez JavaScriptu apod. V ActionScriptu 3.0 je řešení jednoduché, jak možné se dočíst v článku: Zarovnání objektů, detekce opuštění scény na flash.cz. V ActionScriptu 2.0 v3ak nejsou k dispozici odpovídající nástroje. Jak tedy "na to"?

Zdánlivě jednoduché, leč ne vždy funkční řešení:

Pro detekci opuštění Stage kursorem myši se nabízí zdánlivě jednoduché řešení: do Flash animace umísti neviditelný obdélník, který bude od krajů Stage vzdálen na všech stranách o mezeru, jejíž šířku zvolíme nějak rozumně. Pak stačí jen testovat kolizi polohy kursoru myši s tímto obdélníkem. Je-li kursor myši s obdélníkem v kolizi, je zřejmé, že uživatel se myší pohybuje nad Flash animací. Je-li kursor mimo obdélník, tak je uživatel na okraji animace, takže na nic uvnitř animace nemíří, nebo ji kursorem dokonce opustil. Proto je šířku mezery nutno zvolit rozumně, na 10-20% šířky Stage Flash Animace.

Funguje to skvěle. Tedy až do okamžiku, kdy uživatel odjede z Flash animace příliš rychle. Musíme si totiž uvědomit dvě skutečnosti. Za prvé, že testování kolize kursoru myši s obdélníkem uvnitř probíhá pouze jednou za časový interval, který je daný rychlostí přehrávání snímků Flash animace. Délka intervalu se v praxi pohybuje v řádu desetin až setin sekundy, což je na počítačové poměry dlooooouhá doba, během které může kursor myši poskočit o stovky pixelů. Za druhé, jakmile kursor myši opustí Stage Flash Animace, zůstane v proměnných _xmouse a _ymouse, vracejících aktuální polohu kursoru myši, "viset" poslední známá poloha myši před opuštěním Stage a tato poloha by mohla být stále "kolizní".

Co s tím? Zeptal jsem se v diskusi na flash.cz a od Petka jsem dostal tip, jak by to asi mohlo jít. Ten tip vedl k řešení, které se zatím ukazuje jako plně funkční. Díky Petko!

Tedy, jak na to:

Zapomeňme na testování kolize s obdélníkem. Nebudeme to potřebovat. Budeme si naopak do nějaké proměnné typu pole ukládat současnou [1] a předchozí [0] polohu kursoru myši. Budeme to dělat vždy v rámci události onEnterFrame, tedy každý okamžik, kdy Flash animace snímá polohu kursoru myši. Například takto:

_root.mouseX = new Array(0, 0);
_root.mouseY = new Array(0, 0);
_root.onEnterFrame = function()
{
_root.mouseX[0] = _root.mouseX[1];
_root.mouseX[1] = _root._xmouse;
_root.mouseY[0] = _root.mouseY[1];
_root.mouseY[1] = _root._ymouse;
};

Kdykoli pak nastane událost onEnterFrame vypočteme možnou budoucí [2] polohu kursoru a jakmile nám tato předpovězená poloha vyskočí ze Stage Flash animace, víme, že uživatel opustil Flash. Tento výpočet provedeme vždy před tím, než nastavíme nové hodnoty [0] a [1], takže:

_root.mouseX = new Array(0, 0);
_root.mouseY = new Array(0, 0);
_root.mouseOutOfStage = false;
_root.onEnterFrame = function()
{
_root.mouseX[2] = _root.mouseX[1] + (_root.mouseX[1] - _root.mouseX[0]);
_root.mouseY[2] = _root.mouseY[1] + (_root.mouseY[1] - _root.mouseY[0]);
if ((_root.mouseX[2] > Stage.width or _root.mouseX[2] < 0) or (_root.mouseY[2] > Stage.height or _root.mouseY[2] < 0))
{
_root.mouseOutOfStage = true;
}
_root.mouseX[0] = _root.mouseX[1];
_root.mouseX[1] = _root._xmouse;
_root.mouseY[0] = _root.mouseY[1];
_root.mouseY[1] = _root._ymouse;
};

Jednoduché že? Jenže co se situací, kdy uživatel vykoná uvnitř Flash animace pohyb s dost velkým krokem kursoru myši, aby to uvedený script vyhodnotil, jako úmysl opustit oblast Stage Flash animace, ale zastaví jej ještě uvnitř? A jak zahrnout funkčnost, která by nastavila zpětně _root.mouseOutOfStage = false;, když dojde k takové situaci, resp. i tehdy, když se uživatel nad oblast Stage kursorem opět vrátí?

Naštěstí je v ActionScriptu 2.0 k dispozici detekce události, jestli se kursor myši pohnul, či nikoli, a ta nám pomůže vše velmi jednoduchým způsobem vyřešit.

Výsledné řešení detekce opuštění Stage Flash Animace kursorem myši tedy vypadá takto:

_root.mouseX = new Array(0, 0, 0);
_root.mouseY = new Array(0, 0, 0);
_root.mouseOutOfStage = false;
_root.onEnterFrame = function()
{
_root.mouseX[2] = _root.mouseX[1] + (_root.mouseX[1] - _root.mouseX[0]);
_root.mouseY[2] = _root.mouseY[1] + (_root.mouseY[1] - _root.mouseY[0]);
if ((_root.mouseX[2] > Stage.width or _root.mouseX[2] < 0) or (_root.mouseY[2] > Stage.height or _root.mouseY[2] < 0))
{
_root.mouseOutOfStage = true;
}
_root.mouseX[0] = _root.mouseX[1];
_root.mouseX[1] = _root._xmouse;
_root.mouseY[0] = _root.mouseY[1];
_root.mouseY[1] = _root._ymouse;
};
_root.onMouseMove = function()
{
_root.mouseOutOfStage = false;
};

Toto řešení je založeno na předpokladu, který bude zřejmě oprávněný: když už uživatel z rychlého pohybu zabrzdí, takže Stage Flash animace přeci jen na poslední chvíli neopustí, ač by se to z predikce pohybu kursoru myši mohlo zdát, nestihne "zarazit" pohyb kursoru náhle. Než úplně zastaví, udělá kursorem myši ještě alespoň malý "krůček", který nás díky události onMouseMove opět "vrátí do hry".

Pomocí výpočtu předpokládané budoucí polohy bychom zpomalení pohybu mohli zjistit také a pomocí dalších podmínek if ošetřit, ovšem takto je výsledný script přeci jen jednodušší a navíc funguje i pro detekci opětovného najetí kursoru nad Stage.

Doplnění

Při testování se ukázalo, že uvedené řešení nevyhoví v případě, kdy uživatel opustí Stage Flash animace velmi pomalým pohybem myši. Tento problém však můžeme lehce odstranit tak, že se v ActionScriptu nebudeme testovat překročení samotných hranic Stage, ale budeme testovat překročení hranic obdélníkové oblasti odsazené od okraje Stage rovnoměrně po celém obvodu o určitou malou mezeru. Upravený script pak může vypadat takto:

_root.mouseX = new Array(0, 0, 0);
_root.mouseY = new Array(0, 0, 0);
_root.mouseOutOfStage = false;
_root.stagePadding = 2;
_root.mouseXmin = _root.stagePadding;
_root.mouseXmax = Stage.width - _root.stagePadding;
_root.mouseYmin = _root.stagePadding;
_root.mouseYmax = Stage.height - _root.stagePadding;
_root.onEnterFrame = function()
{
_root.mouseX[2] = _root.mouseX[1] + (_root.mouseX[1] - _root.mouseX[0]);
_root.mouseY[2] = _root.mouseY[1] + (_root.mouseY[1] - _root.mouseY[0]);
if ((_root.mouseX[2] < _root.mouseXmin or _root.mouseX[2] > _root.mouseXmax) or (_root.mouseY[2] < _root.mouseYmin or _root.mouseY[2] > _root.mouseYmax))
{
_root.mouseOutOfStage = true;
}
_root.mouseX[0] = _root.mouseX[1];
_root.mouseX[1] = _root._xmouse;
_root.mouseY[0] = _root.mouseY[1];
_root.mouseY[1] = _root._ymouse;
};
_root.onMouseMove = function()
{
_root.mouseOutOfStage = false;
};

21. června 2008

Ukládání komplexních stavových informací ve Flash aplikaci


Představte si, že ve Flashi vytváříte nějakou složitou aplikaci: například katalog produktů nebo e-shop. Aplikace umí zobrazovat katalogové listy produktů a přehledy produktů tříděné či filtrované podle různých kritérií, a to v různé formě (textový výpis zkrácený či podrobný, nabídku produktů s náhledy apod.) Aplikace také umí uživateli nabídnout panely pro nastevní třídících kritérií. Dále tu naleznete také seznamy prodejen, detailní informace o jednotlivých prodejnách. Mohou tu být i jiné informace jako certifikáty, návody a pod. Je to prostě spousta stránek s různou strukturou dat, různým obsahem a formátovaných podle různých šablon.

Vy přitom chcete, aby si tato aplikace uchovávala historii pohybů uživatele a dovolovala uživateli se vracet zpět a nabízet mu vždy správně zkompletovanou stránku se správným obsahem. Je tedy jasné, že si aplikace musí "pamatovat" ke každému kroku uživatele množství různorodých dat, které navíc budou mít pokaždé jinou strukturu. Ukážu Vám, jak to lze zařídit pomocí jednorozměrného indexového pole.

Stačí si uvědomit, že každá stránka aplikace je popsána konečným souborem parametrů. Soubor je sice pokaždé jinak strukturovaný, ale to hlavní je, že je konečný a struktura souboru je podobná pro podobný typ stránek. Mezi parametry uváděné v souboru patří například název šablony stránky (karta produktu, přehled s náhledy, ...) a dále soubor parametrů, které jednoznačně určují obsah (například ID produktu nebo soubor hodnot výběrových kritérií + číslo stránky přehledu).

Všechny tyto parametry můžeme pokaždé spojit do jednoho řetězce - "serializovat" je - a to podle nějakého námi zvoleného schématu. Serializovaný řetězec pak vždy uložíme příkazem push() na konec pole, které si specielně pro účel uchovávat historii vytvoříme.

Pole se bude jmenovat například historie_arr a každý záznam v něm bude odpovídat jednomu kroku uživatele. Informace první stránka aplikace) bude uložena na indexu 0, další bude mít index 1 atd. Poslední stránka bude dosažitelná na momentálně nejvyšší hodnotě indexu - historie_arr[n].

V aplikaci pak umístéme tlačítko pro pohyb zpět v historii. Na toto tlačítko "pověsíme funkčnost", která pomocí akce historie_arr.pop() přečte poslední záznam v poli historie a odstraní ho. Jelikož tento záznam obsahuje veškeré potřebné údaje, stačí ho pak už jen rozparsovat a podle získaných dat zrekonstruovat požadovanou stránku.

Možností, jak data serializovat je mnoho. V některém z příštích článků Vám popíšu docela šikovnou metodu, kterou jsem odkoukal od přírody.

Rozměry prvků v knihovně Flash editoru

Základní grafické prvky můžete mít uložené v knihovně symbolů přímo ve velikosti, v jaké je pak ve Flash animaci používáte. Například logo. Pokud má být ve finále široké 83 pixelů, bude mít tutéž velikost i v knihovně. Logo pak stačí jen přetáhnout na určené místo ve Flash animaci a nic více neřešit. Určité potíže nastanou jen, když chcete rozměry loga později proporcionálně zmenšit, a to například tak, aby jeho šířka byla přesně 64 pixelů. Musíte si vzít k ruce kalkulačku a vypočítat si novou výšku ručně a vypočtenou hodnotu zadat do panelu Info. Zabere to sice pár desítek vteřin, ale proč to nedělat chytřeji.

Osvědčil se mi postup, kdy mám prvek uložený v knihovně tak, aby alespoň jeden jeho charakteristický rozměr (šířka či výška) byl roven právě 100 pixelů. Po umístění prvku do Flash animace pak stačí nastavit na panelu Transform procentuální měřítko hodnotou, která odpovídá požadovanému rozměru v pixelech a druhý rozměr se nastaví automaticky ve správné proporci - tedy pokud máme na panelu zatrženou volbu proporcionální změny rozměrů (Constrain).

Logo si tedy uložíme tak, aby jeho šířka v khihovně byla 100px. Po přetažení loga na určené místo v animaci pak pocí panelu Transform nastavíme velikost instance loga na 83% resp. 64%. Šířka tak bude požadovaných 83px resp. 64px a výška se vždy dopočítá automaticky.

Tento postup je samozřejmě nejvhodnější pro vektorové objekty.

Import dat výkresu z AutoCADu do editoru Flashe

Pokud potřebujete importovat do Flash editoru grafiku z výkresu AutoCADu, bude se Vám možná hodit následující postup. Dopracoval jsem se k němu na základě rad, které jsem získal v diskusi. Potřeboval jsem přenést do Flashe čárovou vektorovou grafiku, kterou jsem vytvořil v AutoCADu. Jednalo se rovinný axonometrický průmět vytvořený ze 3D modelu pomocí příkazu AutoCADu _solprof. Cesty přes formát DXF nebo AI zklamaly - entity jiných typů, než úsečka, kružnice, oblouk, tzn. elipsy a spline křivky se deformovaly. Naštěstí se našlo řešení.

1.krok - AutoCAD 2007: Vytvoření zdroje.
Je to 2D čárový rovinný axonometrický průmět 3D modelu ve formátu DWG AutoCAD 2007. Kolem grafiky je rámeček v proporcích budoucí scény ve Flashi, mj. pro pozdější nastavení správného měřítka. V mém případě má rámeček velikost S x V = 700 x 800 kreslících jednotek, protože výsledná velikost ve Flashi má být 700 x 800 px. Soubor DWG obsahuje jen čárovou grafiku 2D průmětu složenou z entit typu úsečka, oblouk, kružnice, křivka, spline a elipsa. Pro vytvoření průmětu jsem nastavil promítání do roviny a odstranění duplicit při překrývání se čar. (AutoCAD umožňuje i protorové rozložení obrysových čar, ale to nemá praktický význam, pokud chcete vygenerovat čárovou grafiku pro výkres).

2.krok - Adobe Ilustrator CS3: Načtení DWG souboru do prázného web dokumentu.
Otevřel jsem nový "web document" o rozměrech 700 x 800 px a pomocí příkazu Place vložil DWG. Měřítko jsem nastavil na "Scale to Fit Crop An". Tím se kreslící jednotky AutoCADu převedou 1:1 na pixely a grafika včetně rámečku se vystředí automaticky v dokumentu.

3.krok - Adobe Ilustrator CS3: Export do SWF.
Dokument není nutno ukládat. Postačí jej exportovat do SWF. Nastavil jsem přitom formát Flash Player 8.

4.krok - Flash 8 Profesional: Import SWF.
Importoval jsem SWF přímo na scénu. Pak jsem jej příkazem ungroup rozložil až na úroveň entit. Nastavil jsem pak u všech čar tloušťku hairline. Barvy zůstaly zachovány: v AutoCADu jsem používal barvy z množiny 16-ti základních, a ty se touto cestou bez problémů převedly.