Forgrenet navigation i Flash

AttachmentSize
forgrenet_navigation.zip59.05 KB

Denne artikel giver dig en gennemgang af forgrenet navigation. Denne type af navigation gør det muligt at navigere til et givet antal destinationer på kryds og tværs af hinanden, fra en hvilken som helst side på sitet.

Dette er ideelt, hvis brugerinteraktion på én side "åbner" nye muligheder, eller kræver præsentation af yderligere information.

Inden du kaster dig ud i opgaven, bør du læse om lineær navigation på http://wiki.hjaelpmignu.dk/index.php?title=Metoder_til_forgrenet_navigation

Opgavefiler

For at følge denne tutorial skal du hente filen forgrenet_navigation.zip (se nederst på siden).

Vis filen du skal arbejde med

Opgavefilerne indeholder en række swf-filer (side1.swf til side5.swf) Det er de undersider du skal hente ind og kunne navigere frem og tilbage imellem. Derudover er der en fil, kaldet "start.fla". åbn den, da det er den, der skal gøre det ud for "hovedsiden" og hente alle de underliggende swf-filer ind og vise dem. Filen start.fla er fuldstændig blank. Det eneste der er sket her er at "layer 1" er ændret til "Actions". Derudover er der et symbol i biblioteket (menuknap), der skal bruges til menuknapperne.

Opbyg et array med siderne

Stil dig i første frame på laget "Actions" og vis panelet Actions via Window>Actions F9 (Win) Option+F9 (Mac)

Det første du skal gøre er at danne et array (datasæt) der indeholder oplysninger om alle siderne, og deres relation til hinanden. Du bygger først ét array op kaldet pages. Derefter opretter du enkelte array's til hver side, med information om sidens titel, filnavn, samt et valgfrit antal henvisninger til andre sider. Det ser således ud:

  1. var pages:Array = []; // Starter array til alle sider
  2. var side1:Array = ["side 1", "side1.swf", 2, 3];
  3. var side2:Array = ["side 2", "side2.swf", 3];
  4. var side3:Array = ["side 3", "side3.swf", 1, 4, 5];
  5. var side4:Array = ["side 4", "side4.swf", 3, 5];
  6. var side5:Array = ["side 5", "side5.swf", 1];
  7. pages.push(side1, side2, side3, side4, side5);
  8.  
  9. var currentPage:int = 0;
  10. var subPagesOffset:int = 2;

Alt, hvad der gøres her er udmærket beskrevet i artiklen "Opgave i lineær navigation". Se denne for en gennemgang af opbygning af arrayet. Forskel er der dog. Efter de to første "faste" variabler, er der stort set frit slag til at fylde henvisninger til andre side ind i array'et.

Derudover er der en variabel kaldet subPagesOffset, der er sat til det indeks, som henvisningerne starter på. Det gør det nemmere at "regne" sig frem til positionen senere hen i koden, og letter opdateringen, hvis der skal flere faste elementer ind i array'et.

Opret containere til undersider og navigation

Det næste der skal til, i klargøringsfasen, er to containere, der skal indeholde de elementer der arbejdes med. Sprite-objektet container skal indeholde undersiderne, og navigation skal indeholde menuknapperne.

  1. var container:Sprite = new Sprite();
  2. addChild(container);
  3.  
  4. var navigation:Sprite = new Sprite();
  5. addChild(navigation);
  6. navigation.y = 370;
  7. navigation.x = 10;

Som du kan se bliver menuknappen placeret på scenen med egenskaberne x og y. Du kan også regne en placering ud med stage.stageWidth og stage.stageHeight, men her er de bare sat direkte, til statiske værdier.

Lidt forarbejde inde koden til menuknapperne

Før du går i gang med at kode det variable antal punkter i menuen, er der lidt der skal forberedes. Først skal menuknappen gøres klar til at blive sat ind på scenen. Derefter skal du oprette to hjælpefunktioner, der skal varetage dele af koden, der skal være med til at øge overskueligheden i skabelsesprocessen.

  1. HØJREKLIK på menuknap i biblioteket, og vælg Linkage fra menuen.
  2. Skriv MenuButton i feltet Class og tryk OK for at lukke dialogboksen.

Dette gør, at du nu kan bringe dette movie clip til scenen ved at instantiere den, som du gør med alle andre displayobjekter (i stil med var minMenu:MenuButton = new MenuButton()).

Det næste du skal have klar inden , er to funktioner, der ser således ud:

  1. function clearList(obj:DisplayObjectContainer):void
  2. {
  3. while(obj.numChildren > 0)
  4. {
  5. obj.removeChildAt(0);
  6. }
  7.  
  8. }
  9.  
  10. function getPageLabel(pageID:int):String
  11. {
  12. return pages[pageID][0];
  13. }

Den første funktion, kaldet clearList() modtager én parameter af typen DisplayObjectContainer. En while()-løkke bliver ved med at gentage sig selv, sålænge der er display objekter i den. Inde i løkken vil objekterne blive slettet ét ad gangen med obj.removeChildAt(0).

NB: Du fjerner fra indeks 0, fordi display listen er bygget op på en sådan måde, at der ikke kan være luft imellem placerede objekter i display listen. Ved at fjerne fra 0, vil alle ovenliggende objekter rykke én position nedad. Så snart, der ikke er flere objekter, vil while()-løkkens betingelse ikke være opfyld mere.

Den næste funktion (getPageLabel) returnerer værdien af indeks 0, som er titlen på siden. Det kan virke underligt, at have funktioner, der kun udfører én kommando og returnerer noget, der ligeså godt kunne være skrevet med én enkelt linje, det sted den kaldes fra. Der er dog flere årsager til at lave disse "små" funktioner:

  • Det er nemmere at forstå, hvad en kode udfører når der står getPageLabel(subPageID-1) end pages[subPageID-1][0]
  • Du kan senere få brug for at udvide funktionaliteten, så den f.eks. sørger for at titlen maks. indeholder et vist antal karakterer, eller er ene små bogstaver osv.
  • Der er derfor både forståelses- og udvidelsesmæssige grunde til at oprette disse små hjælpere

TIP: I denne artikel er der flere oplagte kandidater, der kunne overføres til funktioner. Du er velkommen til at raffinere koden bagefter. Jeg har valgt at vise denne ene, for at give dig en fornemmelse af mulighederne.

Oprettelse af menupunkter

Selve nerven i denne artikel, er oprettelsen af menupunkterne til navigation. Det der skal løses i denne funktion er følgende:

  1. Rydde al gammel information i navigationscontaineren
  2. Finde ud af, hvor mange menupunkter, der skal tegnes.
  3. Oprette en løkke, der gennemløber alle punkterne og udfører følgende:
    1. Opretter en udgave af MenuButton()
    2. Placerer det i en række (eventuelt efter tidligere placerede punkter)
    3. Skriver linkets titel i menupunktet.
    4. Opbevarer navnet på filen, der skal hentes som punktets instance name.
    5. Opretter en listener, der lytter efter klik
    6. Tilføjer den til navigation's display list.

Koden til dette ser således ud:

  1. function makeSublinks(page:int):void
  2. {
  3. clearList(navigation);
  4. var numSubPages = pages[page].length -subPagesOffset;
  5. var padding:int = 5;
  6. trace(pages[page][1]);
  7. for (var i:int = 0; i < numSubPages; i++)
  8. {
  9. var subPageID = pages[page][i+subPagesOffset];
  10. trace("subPage er " + subPageID);
  11.  
  12. var link:MenuButton = new MenuButton();
  13. link.x = 0+(i*link.width)+(i*padding);
  14. link.y = 0;
  15. link.label_txt.text = getPageLabel(subPageID-1);
  16. link.label_txt.mouseEnabled = false;
  17. link.name= String(subPageID-1);
  18. trace("name: "+link.name)
  19. link.addEventListener(MouseEvent.CLICK, loadPage);
  20. navigation.addChild(link);
  21. }
  22. }

Lad os prøve at brække funktionen makeSubLinks() op i mindre bidder. Som udgangspunkt modtager den, den aktuelle sides indeks som parameter. Derefter sker der følgende:

  1. clearList(navigation);
  2. var numSubPages = pages[page].length -subPagesOffset;
  3. var padding:int = 5;

Først kaldes clearList() med navigation som parameter. Dette vil fjerne alle udgaver af tidligere menuknapper, så du kan tegne en ny række. Derefter gemmes antallet af links i variablen numSubPages. Her bruges subPageOffset til at trække antallet af "faste" emner fra array'ets længde. Endelig oprættes en variabel kaldet padding, der definerer luften mellem menupunkterne. Ved at oprette det som en variabel vil det være nemmere at justere luften senere, hvis der skulle være brug for det. Bagefter løber en for()-løkke, der løber så mange gange som der er undersider:

var subPageID = pages[page][i+subPagesOffset];

Først overføres indekset af sidehenvisningen til variablen subPageID - det er nemmere at huske.

var link:MenuButton = new MenuButton();

Derefter oprettes en udgave af MenuButton() gemt i objektet link

  1. link.x = 0+(i*link.width)+(i*padding);
  2. link.y = 0;

Derefter sættes knappens x- og y-koordinat. Y-koordinatet er egentlig overflødigt, da det som udganspunkt er 0. X-koordinatet kræver dog lidt beregning. Hvis du tænker på,at variablen i starter på 0 giver det første gennemløb af løkken resultatet 0. Næste gang er i = 1. Dette vil give mellemregningen bredden af knappen + 1 x padding. Næste gang er det 2 x knappens bredde + 2 x padding osv.

Resultatet er at knapperne placeres jævnt med padding's værdi som mellemrum.

link.label_txt.text = getPageLabel(subPageID-1);

Så kaldes hjælpefunktionen getPageLabel() og overfører resultatet til det dynamiske tekstfelt (kaldet label_txt), placeret inde i menuknappen.

  1. link.label_txt.mouseEnabled = false;
  2. link.name= String(subPageID-1);

Her deaktiverer du muligheden for at lade tekstfeltet reagere på mouse events. Dette giver frit spil til knappen nedenfor, så den har det fulde ansvar for event'en. Defter kommer der lidt "fusk". Teknikken her er at gemme indekset til undersiden i selve knappen. Da movie clips ikke kan indeholde dynamisk oprettede variabler bruger du her propery'en name, der er lig med clippets instance name. Du trækker én (1) fra, da array'et starter fra

nul (0)

.

NB: Hvis du skal gemme flere værdier, bør du oprette en klasse til MenuButton - flash kigger faktisk efter den. Hvis du vil fuske videre, kan du også oprette usynlige tekstfelter inde i knappen og fylde tal du skal huske ind i dem ... men det er ikke noget jeg har lært dig

  1. link.addEventListener(MouseEvent.CLICK, loadPage);
  2. navigation.addChild(link);

Til sidst, tilknyttes en listener til knappen og den placeres i navigation's display list.

VIGTIGT:, Selv om man fjerner menupunkterne forsvinder de ikke fra playerens hukommelse. For nemhedens skyld, er jeg ikke gået ind på teoriserne omkring "garbage collection", som er et vigtigt emne, når der arbejdes med dynamisk genereret indhold. Du kan bruge "weak reference", når du bruger addEventListener() for at hjælpe med oprydningen. I det tilfælde skal du taste et par ekstra parametre, når du opretter din listener, nemlig link.addEventListener(MouseEvent.CLICK, loadPage, false, 0, true)

Hent og vis undersiden

Det sidste, der skal udføres, minder i store træk om det der gennemgås i opgaven om lineær navigation, så den vil jeg ikke gå for meget i detaljer med. Har du brug for en mere detaljeret gennemgang kan du se den dér.

Den sidste del af koden, ser således ud:

  1. function loadPage(e:MouseEvent):void
  2. {
  3. trace(e.target);
  4.  
  5. var filename:String = pages[e.target.name][1];
  6. var loader:Loader = new Loader();
  7. trace("loading page " + filename);
  8. var url:URLRequest = new URLRequest(filename);
  9. trace("Før load");
  10. loader.load(url);
  11. trace("Efter load");
  12. loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onPageLoad);
  13. trace("url: "+url.url);
  14. makeSublinks(e.target.name);
  15. }
  16.  
  17. function onPageLoad(e:Event)
  18. {
  19. var success = clearList(container);
  20. container.addChild(e.target.content);
  21. trace("placed on stage");
  22. }

Et par udvalgte linjer kræver nok lidt yderligere kommentarer. Først i loadPage() finder du denne linje:

var filename:String = pages[e.target.name][1];

Først drager du nytte af den smule "fusk" der blev lavet i funktionen makeSubLinks() og samler indeksnummeret op på den knap der blev klikket på med e.target.name. Selve filnavnet findes i indeks 1.

Derefter udføres en helt normal loader-rutine, der henter filen og opretter en listener til Loader.contentLoaderInfo(), der affyres, når filen er helt hentet. Til sidst i funktionen står der:

makeSublinks(e.target.name);

Den kalder makeSubLinks() med den klikkede knaps indeks som parameter (den der er gemt i name-property'en). Funktionen onPageLoad() kaldes, når siden er hentet ind. Den rydder først objektet container og tilføjer, den hentede underside til dens display list.

Plads til forbedringer

Nu skulle det hele virke. Der er lidt du kan gøre for at forbedre løsningen. For det første, hentes der ikke nogen side ind til at starte med. Dit problem her er, at al koden, der henter siden er pakket ind i den funktion, der er tilknyttet menuknapperne. Du kan oprette en funktion kaldet showPage(), der efterfølgende kalder loadPage(). På den måde, vil du kunne trække koden til at hente sider, ud af knappen listener - det gør det nemmere at kalde den andre steder fra.

Et kig på opgaven om lineær navigation, kan give dig et fingerpeg, om hvordan det kan løses. Til sidst får du lige al koden i sin helhed.

  1. var currentPage:int = 0;
  2. var subPagesOffset:int = 2;
  3.  
  4. var pages:Array = []; // Starter array til alle sider
  5. var side1:Array = ["side 1", "side1.swf", 2, 3];
  6. var side2:Array = ["side 2", "side2.swf", 3];
  7. var side3:Array = ["side 3", "side3.swf", 1, 4, 5];
  8. var side4:Array = ["side 4", "side4.swf", 3, 5];
  9. var side5:Array = ["side 5", "side5.swf", 1];
  10. pages.push(side1, side2, side3, side4, side5);
  11.  
  12. var container:Sprite = new Sprite();
  13. addChild(container);
  14.  
  15. var navigation:Sprite = new Sprite();
  16. addChild(navigation);
  17. navigation.y = 370;
  18. navigation.x = 10;
  19.  
  20. makeSublinks(currentPage);
  21.  
  22. function makeSublinks(page:int):void
  23. {
  24. clearList(navigation);
  25. var numSubPages = pages[page].length -subPagesOffset;
  26. var padding:int = 5;
  27. trace(pages[page][1]);
  28. for (var i:int = 0; i < numSubPages; i++)
  29. {
  30. var subPageID = pages[page][i+subPagesOffset];
  31. trace("subPage er " + subPageID);
  32.  
  33. var link:MenuButton = new MenuButton();
  34. link.x = 0+(i*link.width)+(i*padding);
  35. link.y = 0;
  36. link.label_txt.text = getPageLabel(subPageID-1);
  37. link.label_txt.mouseEnabled = false;
  38. link.name= String(subPageID-1);
  39. trace("name: "+link.name)
  40. link.addEventListener(MouseEvent.CLICK, loadPage);
  41. navigation.addChild(link);
  42. }
  43. }
  44.  
  45. function getPageLabel(pageID:int):String
  46. {
  47. return pages[pageID][0];
  48. }
  49.  
  50. function loadPage(e:MouseEvent):void
  51. {
  52.  
  53. trace(e.target);
  54.  
  55. var filename:String = pages[e.target.name][1];
  56. var loader:Loader = new Loader();
  57. trace("loading page " + filename);
  58. var url:URLRequest = new URLRequest(filename);
  59. trace("Før load");
  60. loader.load(url);
  61. trace("Efter load");
  62. loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onPageLoad);
  63. trace("url: "+url.url);
  64. makeSublinks(e.target.name);
  65. }
  66.  
  67. function onPageLoad(e:Event)
  68. {
  69. var success = clearList(container);
  70. container.addChild(e.target.content);
  71. trace("placed on stage");
  72. }
  73.  
  74. function clearList(obj:DisplayObjectContainer):void
  75. {
  76. while(obj.numChildren > 0)
  77. {
  78. obj.removeChildAt(0);
  79. }
  80.  
  81. }

Comments

Post new comment

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>, <as3>, <javascript>, <php>. The supported tag styles are: <foo>, [foo].
  • You may link to webpages through the weblinks registry
  • You may quote other posts using [quote] tags.
  • Textual smileys will be replaced with graphical ones.

More information about formatting options

CAPTCHA
Denne test er for at sikre at du er en reel bruger i stedet for en automatisk forespørgsel på en side. Spørgsmålet er for at undgå spam af sitet - tak for din forståelse
Image CAPTCHA
Enter the characters shown in the image.