The example above shows a menu with two levels: a category level, and an individual photo level. When a category is clicked, the categories slide to accomodate the selected category's submenu and that submenu is displayed, with its elements tweened in sequentially. If the submenu is already displayed, clicking the category again has no effect. Clicking a submenu item causes the corresponding photo to be centered on the right and alpha-tweened from 0 to 100.
All of the menu tweening is done with the Tween class, using its onMotionFinished property to delay the display of the submenu til all category tweening is complete. Menu elements are attached dynamically using attachMovie. There are two menu item clips in the library, with linkage names mainopt and subopt.
An actionscript construct which reflects the two-level structure of the menu is an array of objects, where each element of the array contains an object describing the category (label, saved as property lbl) and an array of suboptions (property subopts). Each element of the subopts array is a string, but could be changed to an object with its own properties (eg, lbl and url, to display a menu option other than the actual photo url). This menu could easily be populated from an XML file also:
var menuItems:Array = [
{lbl:"Hands",
subopts:["mouse.jpg", "pen.jpg", "paper.jpg", "chain.jpg"]},
{lbl:"Objects",
subopts:["keys.jpg", "binoculars.jpg", "lightbulb.jpg"]},
{lbl:"People",
subopts:["umbrella.jpg", "table.jpg", "phone.jpg"]},
{lbl:"Technology",
subopts:["keyboard.jpg", "laptop.jpg"]}
];
When a new category is selected from the menu, each of the elements that needs to be moved to accommodate the new submenu does so, after its new position is calculated. To do this, the program looks at the position of each category item and calculates the amount it needs to be moved up or down. All category items requiring tweening are tweened at once, and when they are in place, function showSubopts is called to tween in the submenu. The function which removes the previous submenu, calculates the new category item positions and tweens them to their new locations looks like this (assigned to the onRelease property of the category menu item):
function movemenuItems() {
var newsubopts:Number;
var newpos:Number;
var tw:Tween;
// if not already selected
if (this != mainSelected) {
// remove old suboptions (assume maximum of 10)
for (var i=0; i < 10; i++) {
if (this._parent["subopt"+i] != undefined) {
this._parent["subopt"+i].removeMovieClip();
}
}
// get number of suboptions to be opened (to find amount to move menu items below)
newsubopts = menuItems[this.myi].subopts.length;
// for each mainopt, if above or equal current selection then y = 25*i
// else y = 25*i + newsubopts * 23
for (var i=0; i < menuItems.length; i++) {
if (i <= this.myi) newpos = 10 + 25 * i;
else newpos = 10 + 25 * i + newsubopts * 23;
if (this._parent["mainopt"+i]._y != newpos) {
tw = new Tween(
this._parent["mainopt"+i],
"_y",
Strong.easeOut,
this._parent["mainopt"+i]._y,
newpos,
0.7,
true);
// set scope of showSubopts same as this function (so 'this' still = selected main btn)
tw.onMotionFinished = Delegate.create(this, showSubopts);
}
}
mainSelected = this;
}
}
This function assumes the category menu items have previously been attached (and are named mainopt0, mainopt1, etc), and each has a property myi which is the index into menuItems for that category. For each category menu item, if it is above (or is) the currently selected item, its y position is calculated based on its height and the number of menu items above it. For each category menu item below the selected item, its y position is calculated based on the number of category menu items above it plus the height of the submenu.
Variable mainSelected is a main timeline variable used to track which category menu item is currently selected (in case it is clicked again while already open).
Once the main category menu items are in place (with space for the suboptions), a movieclip for each suboption is attached and tweened into place. For each, the label textfield is set with the correct text and a function is assigned to handle click events, which includes setting an indicator of the currently selected clip (and clearing the previous one), and loading the corresponding photo into the photo area.
function showSubopts() {
var mc:MovieClip;
for (var i=0; i < menuItems[this.myi].subopts.length; i++) {
mc = this._parent.attachMovie("subopt", "subopt"+i, 1000+i, {_x:-100, _y:35 + 25 * this.myi + 23 * i});
mc.indicator._visible = false;
mc.txt.text = menuItems[this.myi].subopts[i];
new Tween(mc, "_x", Regular.easeOut, -100, 10, 0.5+i*0.2, true);
mc.onRelease = function() {
subSelected.indicator._visible = false;
subSelected = this;
this.indicator._visible = true;
pic._alpha = 0;
mcl.loadClip("menupics/" + this.txt.text, pic);
}
}
}
Using 0.5+i*0.2 to specify the tween time means that each suboption added to the menu will take a bit longer to tween in than the previous one, produced the sort of staggered fly-in of each of the submenu items.
The two functions and menu array shown above are the heart of the menu system in this movie. Additional movie functionality includes defining variables that are to be globally accessible (mainSelected, subSelected and mcl, eg), setting up a listener with an onLoadInit function for centering and fading in the photo when loaded, and the initial creation and placement of category menu items based on the information in the menuItems array, which looks like this:
for (var i=0; i < menuItems.length; i++) {
var mc:MovieClip = this.attachMovie("mainopt", "mainopt"+i, i, {_x:10, _y:10 + 25*i, myi:i});
mc.txt.text = menuItems[i].lbl;
mc.onRelease = moveMenuItems;
}
// offset the last option a bit so the tween and onMotionFinished will be triggered
mc._y += 0.1;
Complete code and pictures for the sample shown above may be downloaded by subscription from the link at right.
last update: 18 Jul 2006
Discussed on this page:
two level menu with easing, tween class with onMotionFinished, photos fade in
Files:
twolevelmenu.fla
In twolevelmenu.zip, password required (updated 22 Aug 06 to extract to correct directory structure)
Note: if you download this file and don't have the same font I used installed on your machine, choose a font you do have for the textfields in mainopt and subopt movieclips, and embed the font using the Embed button in the Properties panel.
Subscription:
A password may be obtained by subscription ($20 for one month)
An access password will be emailed to the address you specify within 24 hours of receipt of payment, and will remain active for 30 days thereafter. A list of all files currently available at the site may be viewed here.