By
Mike Britton
Tuesday, May 20 2003 12:01 PM
URL:
http://www.zdnetasia.com/builder/program/web/0,39044550,39127820,00.htm
The ability to parse XML and rely on XML-driven data in MX opens up Flash’s
development potential. With this functionality, you can modify menu items by
changing an XML file instead of rehashing your ActionScript.
In this
article, we’re going to construct an interface that uses Flash’s animation power
to display dynamic content. When we’re finished, we’ll have a small piece that
could be used as a product display. You can download the source code for this application here.
What you’ll need
- Flash MX
- The source files for this project
- Intermediate experience in XML and Flash ActionScript
Although
it’s a good idea to separate your ActionScript into includes (#include
yourFile.as) when it becomes too complex, we won’t be doing that here. Our
application is simple enough for us to do most of our coding in the Flash MX
environment. When we’re finished, you may decide to extend what we’ve built in
various ways. If so, you'll want to keep an eye on your code and split it into
external ActionScript files as soon as you feel it’s necessary.
The .fla fileStart by opening Flash MX and loading
xml_interface.fla. Your screen should look like
Figure A.
| Figure A |
 |
| Your project inside Flash
MX |
The elements of this small yet potent
Flash interface have been placed on the screen and given instance names
menu and
pages. Let’s work through a description of each to gain
more knowledge of the elements that make up our interface.
NavigationSince our interface is small, we’ll want to
conserve screen space to focus the user’s attention on the content. Our
interface will contain code for a menu that hides itself after a given period of
time elapses.
Figure B shows the application window with the menu
hidden.
| Figure B |
 |
ContentThe movie clip
pages represents the
content, as shown in
Figure C.
| Figure C |
 |
The movie clip
pages
will be duplicated vertically as many times as there are nodes with the name
page in the XML file, as shown in
Listing A. Inside
pages, each individual page clip
contains two text fields, one to display the page title and another (with a
scroll bar component) to display the page’s text content.
The
XML file in Listing A represents a two-level-deep site structure. Our
ActionScript will load this file and build itself based on the node values and
attributes expressed in content.xml.
Code on the
menuGenerally speaking, it’s not good practice to locate your
ActionScript code directly on movie clips. It makes it more difficult to keep
track of what’s going on, and it makes returning to the project about as
pleasant as a second-degree burn. For our purposes, it’s practical to include
code on movie clip
menu, as you’ll see when you click on it (it’s on the
main stage) and view its code, shown in
Listing B.
The
event handlers in Listing B control the movement of instance
menu. You
can view them in Flash by clicking on the
menu clip on the main
stage.
So how does
menu know what to do and when to do it? On the
main timeline is a function that looks for certain conditions and reports them
to the showMenu() and hideMenu() functions that are embedded on
menu, as
shown in
Table A.
Table A
| 19 |
this.menuMove = function(x) |
| 20 |
var mousePos = x; |
| 21 |
if (mousePos<=20) { |
| 22 |
// Tell menu to show itself |
| 23 |
_root.menu.showMenu(); |
| 24 |
} else if (mousePos>=190) { |
| 25 |
// Tell menu to hide itself |
| 26 |
_root.menu.hideMenu(); |
|
} |
|
} | |
Lines 19 through 28 on the main timeline define conditions for
menu states.
Code on main timeline
On the main timeline,
you’ll see a layer labeled actionscript. Click on its first frame. Let’s
walk through the code to get a better understanding of what’s going
on.
Setting up the arrays and creating the XML
object
Since our project relies on XML for both content and structure,
we’ll create a few array objects to hold data (page content and page titles).
We’ll pull everything we need from the XML file, and the pages will assemble
based on the number of nodes returned in the XML.
On
line 0, the array positions will hold locations in pixels for movie clip
pages. The this keyword allows the code to be easily broken into
its own class. In O-O programming, this technique is often used to denote a
calling object; here, it’s serving a similar purpose, except it's referring to
itself.
In Table B, lines 1 through 3 contain similar arrays for
the major content areas in our application. Array titles holds the page
titles, while array text holds the text content that will go into
scrollable boxes. Array page_content holds all the pages’ XML nodes
collectively.
Line 5 marks the creation of a new XML object to contain
the nodes in the XML file we’ll be loading. It’s important to do this so we can
begin using the native XML properties to make our application come
alive.
Line 6 simply tells our new XML object (myXML) to ignore any white
space encountered between the XML nodes.
Table
B
| 1
|
this.positions =
new Array(); |
| 2
|
this.titles =
new Array(); |
| 3
|
this.text = new
Array(); |
| 4
|
this.page_content = new
Array(); |
| 5
|
this.myXML = new
XML(); |
| 6
|
this.myXML.ignoreWhite =
true; | |
The required arrays and the XML objectSetting variables that need to be available every
frameThe movie clip
pages needs to move vertically by a
certain number of times its own height, depending on which page is currently
chosen. To make it animate into position, a function must run continuously to
keep
pages’ position current.
In
Table C, this function
keeps the site’s content where it needs to be. As the _y position of
pages changes, variable
starty’s value changes. Another important
variable in this formula,
verticalno, is the difference of
pagesy
and
starty. It will be used to animate
pages to a new location on
line 11.
Table C
| 8
|
this.onEnterFrame =
function() { |
| 9
|
starty =
GetProperty(_root.pages,_y); |
| 10
|
verticalno =
pagesy-starty; |
| 11
|
setProperty
(_root.pages, _y, starty+(verticalno/6)); |
| 12
|
}
| |
The function
executes once per frame, so if your movie’s frame rate is 31 f.p.s., it will
execute 31 times. That’s why we want to make sure that this code is as brief as
possible! Some would say it’s bad form to make a function like this execute on
every frame, but because the impact of this interface depends on the way
pages glides from page to page, this controversial use of onEnterFrame is
justifiable.
Setting pages’ location on the stage
We’re going
to set pages’ _y location on the stage from special links on clip menu
(Table D). These links will be in text fields with Render As HTML enabled
in the Properties panel. They will use asfunction(), a documented Flash function
that allows you to call any function in the application using
hyperlinks.
As you already know, pageMove sets movie clip
pages’ location on the stage when it’s hit by HTML-encoded links.
It does this using asfunction().
Table D
| 14
|
this.pageMove =
function(location) { |
| 15
|
trace(location);
|
| 16
|
_global.pagesy =
location; |
| 17
|
}
| |
Depending on the
position of the user’s mouse on the timeline,
menuMove triggers either
menu’s showMenu() or hideMenu() functions. You may want to adjust
menuMove's position numbers to accommodate the area of the interface you
want to reserve to run showMenu(). Obviously, a longer distance from your menu
activation area on the stage would lend a feeling of increased sensitivity to
the interface when used.
Parsing XMLNow
that we’ve coded all the functions necessary, let’s dive into the function
executed when the XML file finishes loading. It’s a doozy.
Every XML
object in Flash MX has an onLoad method. Advanced applications typically use an
init() to initialize classes. We’re going to do something similar, only we'll
use our XML object’s onLoad. When content.xml finishes loading, the code in
Listing C will execute.
Table
E takes this particular onLoad apart.
Table
E
| 32
|
trace("XML
Loaded!"); |
| 33
|
_global.allNodes
= this.firstChild.childNodes; |
| 34
|
_global.hidden =
true; |
|
|
| 36
|
trace("Total
Pages: " + allNodes.length);
| |
Line 32 traces
“XML Loaded!” to the Output Window in the Flash testing environment
([Ctrl][Enter]), basically telling you everything’s loaded and the main
initialization function is being run.
In lines 33 and 34, allNodes is
populated with the first XML node’s children. The global variable
hidden,
set to true, is used by showMenu() and hideMenu() on movie clip
menu.
Remember, allNodes represents the entire structure of this application, content
and all. We will leverage it to build our content and make it
animate.
For each node (or page) in this simple application, the
following things happen:
Line 39: Movie clip
pages is duplicated
and renamed as many times as there are nodes. Each new copy’s instance name
includes variable
j, making each one unique and totally available to your
application.
Line 40: Clip
page (1, 2, 3, etc…) is repositioned on
its _y every 239 pixels as many times as there are nodes.
Line 41:
page (page1, page2, etc.) is given a location property. This means we can
populate an array with the clip instance’s location property. We can also load
the value into the text we’ll soon be outputting to clip
menu’s menu_txt
text field.
Line 42: The title attribute of each node is pushed into
array
titles. This will make each page’s title accessible just as its
location is.
Line 43: The same happens here as with the previous line,
except now the position property of each duplicated page clip is pushed into an
array called, you guessed it,
positions.
Now it’s time to go one
more level in the XML structure and load
menu. In lines 44 through 49, if
the node name of each of allNodes’ children is
page (which it is), each
duplicated
pages movie clip’s pageTitle text field is populated with the
title attribute of each XML node.
To load
menu with each of the
page names from the XML, line 46 sets variable
linkText to each node’s
name attribute. As shown in
Table F,
menu’s text field menu_txt is
set to receive HTML, and
menu itself is pumped with HTML-formatted text
using our
positions array and our
linkText variables.
Table F
| 37 |
j = 0; |
| 38 |
while (j<allNodes.length) { |
| 39 |
duplicateMovieClip(_root.pages["page"], "page"+j, j); |
| 40 |
setProperty(_root.pages["page"+j], _y, j*239);
|
| 41 |
_root.pages["page"+j].location =
-j*239; |
| 42 |
titles.push(allNodes[j].attributes.title);
|
| 43 |
positions.push(_root.pages["page"+j].location); |
| 44 |
if (allNodes[j].nodeName == "page")
{ |
| 45 |
_root.pages["page"+j].pageTitle
=
allNodes[j].attributes.title; |
| 46 |
var linkText =
allNodes[j].attributes.name; |
| 47 |
_root.menu.menu_txt.html
= true; |
| 48 |
_root.menu.menu_txt += "<a
href=\"asfunction:_root.pageMove, "+positions[j]+"\">"+linkText+"</a><br>";
|
| 49 |
} |
| 50 |
j++; |
| 51 |
} | |
The
HTML-formatted text contains links, but instead of linking to Web pages, they
execute a function on the timeline using asfunction(), mentioned earlier in this
article. In line 48, asfunction() passes to the pageMove() function the position
of each duplicated page movie clip:
_root.menu.menu_txt +=
"<a
href=\"asfunction:_root.pageMove,"+positions[j]+"\">"+linkText+"</a><br>";Now
we want to populate the cool scroll boxes on each duplicated page clip with the
node value of each text node in the XML. First, we have to set up a couple of
arrays, as shown in
Table G.
Table G
| 52
|
for (k=0;
k<titles.length; k++) { |
| 53
|
_root.pages["page"+k].title = titles[k]; |
| 54
|
}
|
| 55
|
for (i=0;
i<allNodes.length; i++) { |
| 56
|
page_content[i] =
allNodes[i].childNodes; |
| 57
|
}
| |
Lines 52 through
54 load every duplicated page clip’s title text field with the corresponding key
in the
titles array. You’ll recall in line 42, we loaded
titles
with the title attribute of each node named
page.
Next, we load
Array page_content with the children of allNodes, which represents all the XML
nodes named
page (
Table H).
Table
H
| 58
|
// Our first
loop begins the dig…for (k=0; k<page_content.length; k++) {
|
| 59
|
// Our second
loop makes it serious…for (l=0; l<page_content[k].length; l++) {
|
| 60
|
_root.pages[l].page.contentBox.html
= true; |
| 61
|
// We zero in
for the kill…if (page_content[k][l].nodeName == "text") {
|
| 62
|
// Set the HTML
‘cause you never know…_root.pages["page"+[k]].contentBox.html = true;
|
| 63
|
// Load that
content!_root.pages["page"+[k]].contentBox = page_content[k][l];
|
| 64
|
}
|
| 65
|
}
|
| 66
|
}
| |
Since we loaded
the page_content array with the child nodes (children) of allNodes, we have an
array of objects. These objects contain the text nodes we need to deliver to our
scroll boxes. Obviously, we need to do a few loops to get at the second
dimension of Array page_content, which contains the objects.
Which
reminds me—we never loaded the XML. Let’s load it up now:
this.myXML.load("content.xml"); The resultWhen
published to a test Web page, you should see a smooth-scrolling Flash interface
with a yellow background. It's ideal for product displays, even if you don’t
have access to a database. Give it a try:
Download the
code and get started.