Fisheye Menu Using AS3 and TweenLite

A friend of mine that enjoys bitching until I help her with whatever popped into her head that moment messages me tonight.  “Hey for my site [that you’ll make for me for free because i’m a girl and your friend] I want a menu system like that one I showed you [which you’re supposed to magically remember out of all the sites you’ve seen on the whole internet in your life].” After she re-sent me the link, a beautiful site called ilovedust I took a look at the slick little fish-eye style menu and said, “Yeah that’s Animation, I do code.  Not pretty stuff like that.”

After 5 minutes of receiving complaining message (but mostly once my game was finished that I was playing at Kongregate, I actually thought about what was happening in that menu, and after about 20 minutes of coding, had a workable menu.  I thought I’d post the code here and make a little tutorial out of it.

I love TweenLite.  And TweenMax.  And pretty much everything at GreenSock.  Once you learn the class, it makes everything so easy.  For those that are here who are AS2 coders… a) Upgrade!  and b) They also have AS2 versions of all of their code.  There are a number of other Tween libraries to choose from. Some perform better than others.  But TweenLite was the first for me that ‘made sense’ in my budding AS3 days, which still continue.

This is a quick little example of making a 5 menu-item menu that does stuff when you mouse-over.  You could probably think of a million ways to optimize this code; putting the menu items in a loop to initialize and things like that.  I’m just going to lay it out, and you can make your own better.

First of all, make sure you download the TweenLite Library. You could get the TweenMax library ( size added to flash: 11k ) which comes with a ton of other features.  But all we really need comes in TweenLite ( size added to flash: 3k ).  Unzip TLite and copy everything in the “gs” folder to your working flash directory.

Create a new Flash File ( Actionscript 3 ) and save it.

Create a new Actionscript File and save it into the working directory.  You could get fancy and put it in subfolders.  For this example, I am not going to.  The document class AS3 file will simply be in the main directory.

I’ve named my as3 file “FisheyeMenuExample.as” and heading back to the FLA, we’ll set that as the Document Class.

FishEye Tutorial: Document Class

And then the fun begins!

package {

import flash.text.TextField;
import flash.text.TextFormat;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import gs.TweenLite;

public class FisheyeMenuExample extends MovieClip {

private var menu1:TextField;
private var menu2:TextField;
private var menu3:TextField;
private var menu4:TextField;
private var menu5:TextField;

private var menu1Str:String = "Menu Item 1";
private var menu2Str:String = "Menu Item 2";
private var menu3Str:String = "Menu Item 3";
private var menu4Str:String = "Menu Item 4";
private var menu5Str:String = "Menu Item 5";

private var newFormat:TextFormat = new TextFormat();

/** Vertical Position **/
private var menu1_Y:int = 30;
private var menu2_Y:int = 50;
private var menu3_Y:int = 70;
private var menu4_Y:int = 90;
private var menu5_Y:int = 110;

/** Horizontal Position **/
private var menuX:int = 30;

private var floatX:Number = 1;
private var floatY:Number = 0;

private var speedOut:Number = 0.5;
private var speedIn:Number  = 0.5;

private var menuWidth:Number = 170;

private var zoomScale:Number = 1.2;

public function FisheyeMenuExample():void {
newFormat.align = 'left';
newFormat.blockIndent = 0;
newFormat.bold = true;
newFormat.bullet = true;
newFormat.color = 0x990000;
newFormat.font = "Times New Roman";
newFormat.indent = 0;
newFormat.italic = false;
newFormat.leading = 0;
newFormat.leftMargin = 0;
newFormat.letterSpacing = 4;
newFormat.rightMargin = 0;
newFormat.size = 14;

newFormat.url = "http://www.zombieflambe.com";
//newFormat.target = "_current";
newFormat.underline = true;

menu1 = new TextField();
menu1.text 	= menu1Str;
menu1.name 	= 'txt1';
menu1.x 	= menuX;
menu1.y 	= menu1_Y;
menu1.width = menuWidth;
menu1.addEventListener( MouseEvent.MOUSE_OVER , menuZoomIn );
menu1.addEventListener( MouseEvent.MOUSE_OUT , menuZoomOut );

menu2 = new TextField();
menu2.text = menu2Str;
menu2.name = 'txt2';
menu2.x = menuX;
menu2.y = menu2_Y;
menu2.width = menuWidth;
menu2.addEventListener( MouseEvent.MOUSE_OVER , menuZoomIn );
menu2.addEventListener( MouseEvent.MOUSE_OUT , menuZoomOut );

menu3 = new TextField();
menu3.text = menu3Str;
menu3.name = 'txt3';
menu3.x = menuX;
menu3.y = menu3_Y;
menu3.width = menuWidth;
menu3.addEventListener( MouseEvent.MOUSE_OVER , menuZoomIn );
menu3.addEventListener( MouseEvent.MOUSE_OUT , menuZoomOut );

menu4 = new TextField();
menu4.text = menu4Str;
menu4.name = 'txt4';
menu4.x = menuX;
menu4.y = menu4_Y;
menu4.width = menuWidth;
menu4.addEventListener( MouseEvent.MOUSE_OVER , menuZoomIn );
menu4.addEventListener( MouseEvent.MOUSE_OUT , menuZoomOut );

menu5 = new TextField();
menu5.text = menu5Str;
menu5.name = 'txt5';
menu5.x = menuX;
menu5.y = menu5_Y;
menu5.width = menuWidth;
menu5.addEventListener( MouseEvent.MOUSE_OVER , menuZoomIn );
menu5.addEventListener( MouseEvent.MOUSE_OUT , menuZoomOut );

menu1.setTextFormat( newFormat );
menu2.setTextFormat( newFormat );
menu3.setTextFormat( newFormat );
menu4.setTextFormat( newFormat );
menu5.setTextFormat( newFormat );

addChild( menu1 );
addChild( menu2 );
addChild( menu3 );
addChild( menu4 );
addChild( menu5 );

}

private function menuZoomIn( e:MouseEvent ):void {
var moveX:int = e.target.x + floatX;
var moveY:int = e.target.y + floatY;
TweenLite.to( e.target , speedOut , {x: moveX , y: moveY, scaleX: zoomScale, scaleY: zoomScale});
}

private function menuZoomOut( e:MouseEvent ):void {
var moveX:int = menuX;
var moveY:int;
switch( e.target.name.substr( 3, 1 ) )  {
case '1':
moveY = menu1_Y;
break;
case '2':
moveY = menu2_Y;
break;
case '3':
moveY = menu3_Y;
break;
case '4':
moveY = menu4_Y;
break;
case '5':
moveY = menu5_Y;
break;
}

TweenLite.to( e.target , speedIn , {x: moveX , y: moveY, scaleX:1, scaleY:1});
}
}

}

So, lets go line by line.

  1. import flash.text.TextField;
  2. import flash.text.TextFormat;

These two lines are where we get the TextField from and TextFormat so that we can style the menu items how we like.

11.   private var menu1:TextField;

This line begins declaring the 5 menu items we’ll be adding.

17.  private var menu1Str:String = “Menu Item 1”;

This line begins declaring the Text that will be displayed for those menu items.

23.  private var newFormat:TextFormat = new TextFormat();

This line declares and initializes our TextFormat object that will contain all the styles.

26.   private var menu1_Y:int = 30;

This line begins declaring and initializing the Y (vertical) positioning of each item.  Note they’re 20px apart.  In optimizing, you could simply state a menu_Y position, and a “menu_dY = 20;” variable to represent the change ( d ) in Y coordinates if you’re using a loop.

33.   private var menuX:int = 30;

Specifies the X coordinate that all menu items will start at.

35.   private var floatX:Number = 1;
36.
private var floatY:Number = 0;

floatX and floatY are two variables I’ve set up to represent the position I want the menu items at when they’ve ‘floated’ out as you roll over them.  This probably could’ve been a less confusing name ( datatype float… ).  Oh well.

38.   private var speedOut:Number = 0.5;
39.
private var speedIn:Number  = 0.5;

speedOut represents the total amount of time it takes the menu item to float from original position, all the way out to it’s moused-over-position.  speedIn is the time it takes to go from that position, back to original position.  Ex. Changing these values to 1would mean the menu item, once moused-over, would float out slower, taking a full second to move from original to destination.  Changing these values to 0.1 would mean they would snap/pop quickly out to their destination and back again.

41.   private var menuWidth:Number = 170;

The width of the text box.  I have mine set for the text I’m using here.

43.   private var zoomScale:Number = 1.2;

This variable represents how much the text will ‘zoom’/’scale’ when moused over.  1.2 is essentially 120% it’s original size.

46.   newFormat.align = ‘left’;

Line 46-58 represents a vast number of settings and ways you can format your text.  Most of these you’ll never need to set, but I figured I’d copy/paste from the AS3 Live Docs on TextFormat.

64.   menu1 = new TextField();
65.
menu1.text  = menu1Str;
66.   menu1.name  = ‘txt1’;
67.
menu1.x     = menuX;
68.   menu1.y     = menu1_Y;
69.   menu1.width = menuWidth;
70.   menu1.addEventListener( MouseEvent.MOUSE_OVER , menuZoomIn );
71.   menu1.addEventListener( MouseEvent.MOUSE_OUT , menuZoomOut );

These lines create menu item 1’s TextField (line 64), set the .text display text property to menu1Str ( “Menu Item 1” ), sets the Instance Name of this text field to “txt1” ( which comes in handy later on ), sets the X and Y values, sets the width of the TextField, and adds two EventListeners.  When a user moves their mouse over menu1, I want to call the function menuZoomIn, and when their mouse leaves the boundaries of menu1, I want to call the function menuZoomOut.

Lines 73 – 113 are simply the exact same as above, just for the other four menu items.

115.   addChild( menu1 );

Lines 115 – 119, I am adding each TextField to the stage so they’ll be visible.

123.   private function menuZoomIn( e:MouseEvent ):void {
124.
var moveX:int = e.target.x + floatX;
125. var moveY:int = e.target.y + floatY;
126.       TweenLite.to( e.target , speedOut , {x: moveX , y: moveY, scaleX: zoomScale, scaleY: zoomScale} );
127. }

This is the simple menuZoomIn function that gets called as the mouse moves over any menu item.  Line 123 states that this is a private function ( meaning you could not refer to this function from another class file ).  With any event listener function, you must pass in the event that is taking place ( “e:MouseEvent” ) and at the end of line 123, you’ll notice the “:void” parameter.  This is good practice to do with any function.  :void specifies the type of data (if any) that this function will be returning.  This allows the compiler to parse through your code a little faster if you have specified return types.  If we were returning an int value, we would change “:void” to “:int” for example.

Line 124 declares a local variable (local meaning that it is declared inside this function, and gets destroyed at the end of this function.  moveX is an integer value.  But wait, we declared floatY to be data type “Number”!  What’s going on here?

Basically I am saying ( in the code ) that I don’t really care about precision or decimals.  If for some reason I needed to move the menu item some precise fractional value, I could change “:int” to “:Number” but as it stands, I don’t really need those extra decimal numbers, so specifying moveX as an int means “ignore stuff to the right of the decimal and just give me the whole numbers plzkthx.”  I dislike fractions, and the int data type takes up less memory than the Number data type.  Were this some huge class or a huge loop, ints will process faster.  Good habit to get into.

Line 124 ( once more ) declares moveX and initializes it to “e.target.x + floatX”.  This is saying, “take the X coordinate of whatever menu item our mouse is currently over, e.target, and add the amount we’ve already specified in floatX so we know the destination X coordinate for this specific menu item.”

Line 125 does the same thing, but for the Y coordinate.

LINE 126!  All this text just to get to the fun part… using TweenLite!!

126.  TweenLite.to( e.target , speedOut , {x: moveX , y: moveY, scaleX: zoomScale, scaleY: zoomScale} );

In the TweenLite class, there is a method called “to” and “from”.  To states that whatever parameters I pass in, I want to go from the object’s current position… To the coordinates I specify.  If I had used the “.from” method here, when I moused over a menu item, the menu item would pop out to the moveX, moveY coordinate and the Tween back to it’s original position.  As it stands here, I want to go from the current position of e.target… To moveX, moveY.

The first parameter of the TweenLite.to function takes an object.  “What exactly do you want me to tween?” and this can be any object… a movie clip, sprite, etc.

The second parameter takes a Number.  “In Seconds, how long do you want this Tween to take?”

The third parameter takes another object which consists of a number of parameters that you can specify for the tween.  Everything from Alpha to x,y coords, to scale can be included here.  In my usage here, I’ve specified an x and y coordinate, and an amount that I want to scale the object along the X and Y axes.  See the comments inside the TweenLite.as class file for all the parameters you can pass in.

129.   private function menuZoomOut( e:MouseEvent ):void {
130.
var moveX:int = menuX;
131. var moveY:int;
132. switch( e.target.name.substr( 3, 1 ) )  {
133. case ‘1’:
134. moveY = menu1_Y;
135. break;

menuZoomOut is called when the mouse leaves the menu item’s boundaries.  Like menuZoomIn, it is a private function, taking the MouseEvent parameter, and returning nothing ( :void ).  moveX is declared and initialized just the same.  moveY, however, is different.  Each TextField had a different original Y position, and we need to make sure they return back to the same spot.  Otherwise we’ve gone from Fish-eye menu to crazy-wandering-menu-item-animation menu!

e.target.name will give us the name we specified earlier.  In our example, e.target.name would return “txt1”, for example,  if our mouse left the boundaries of menu1.  In using “e.target.name.substr( 3, 1 )”, the code is saying “give me the Instance Name of the target that we just moused-out from, but I just want a tiny substring from that name… and that substring starts at string index 3 and has a length of 1 character.”  Essentially that code is returning “1”, “2”, “3”… etc for each of the menu items, which, passing that substring through a Switch statement, I’m able to specify a specific moveY based on which menu item we’ve just left.

So, line 133 says, “If that substring you return is the number 1, I want to set moveY equal to menu1_Y, then break out of the switch statement because I’ve found the correct value.”  Lines 136 – 148 simply do the same thing but for the remaining 4 menu items.

Line 150 looks exactly the same as Line 126 with the exception that I’m using “speedIn” as the amount of time I want this Tween to take.  This is because… well… maybe I want different speeds?  You can set the Out and In times separately so your menu might pop out quickly, then slowly return back to its original position.

And that’s it!

Download the FisheyeMenuExample SourceCode

FacebookTwitterGoogle+Share

2 Comments

 Add your comment
  1. Thanks for the tutorial. I trans versed the X & Y to make it a horizontal menu. But its not expanding from the center. Also, can I adapt this to MoveClips instead of text boxes?

  2. Yes, absolutely.. Movie Clips.. Sprites.. you can tween just about anything

Leave a Comment

Your email address will not be published.

1 Trackback

  1. Zombie Flambe » Blog Archive » AS3 FishEye Menu Downloadable Class + TweenLite Example (Pingback)