AS Reference  :  Notes Index  :  Resources  :  About/Contact  :  Downloads

A Two-Level Menu with Easing

How the Menu Works

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.

Array Structure for Menu

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"]}
   ];

Calculating menu item positions

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).

Displaying submenu options

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.

Other movie functionality

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.

Intro
Flash: What & How
Example Sites
Create
Draw, Edit Shapes
Gradients
More Drawing Tips
Import
A Sample
Animate
Frames, Keyframes
Motion Tweens
More Motion Tweens
Shape Tweens
Masks
Control
Stop/Replay
Movieclips Intro
Movieclip Reference
Site Structure 1
Slideshow Movieclip
Contact Form
Scroll Resume
Preloader
Site Structure 2
Publish
Display Options
Player Detection
Optimize
AS 2.0 Basics
Intro to Syntax
Playhead Commands
Playhead Cmds 2
Coded Tween
onEnterFrame
Intro to Classes
Declare/Assign
Comments, Trace
Simple Data Types
Arrays & Objects
Code Blocks
Operators
Beyond Buttons
Code Structure
Toggle Controls
Group of Buttons
Drag and Hit
Distort Magnifier
Scroll Text
Bee Game
Dart Shooter
Sound Control
Easing Slider
Easing Slider 2
Components Intro
Timers & Delays
Dynamic Content
Intro
Drawing API
Create Text
Attach Movieclips
Easing Slider 3
Easing Slider 4
Load jpg/swf
Sliding Viewer
Preload swf
XML
Easing Slider 5
Server Comm
LoadVars (w/ PHP)
AS - PHP Lookup
Text File
Database 1:LoadVars
Database 2:Remoting
Read from directory
AS 2.0 Classes
Intro
Math
Key
Date
Color
EventDispatcher
New Samples
Pie Chart
Event-model Emailer
Tween Sequence
Fuse Sequence
SVG in Flash
Bitmap Topo
SWF as Data Holder
Two-level Menu
Yahoo! Flash Maps
Class-based Game
ASTB Samples
Disclaimer
3D Outlines
Bounce Collide
Address Book
Save Drawings
Home  :  Notes Index  :  Resources  :  About/Contact  :  Downloads