Rotating Menu

 

Sometimes Flash files are used to replace typical HTML tasks on a website, for instance, the menu that represents the „control unit“ and hence some sort of a branch point that connects the single pages of a website with each other by the use of links. In my view, there are generally no certain functional reasons for such a replacement but rather aesthetical reasons: Using ActionScipt based Flash files as a menu, one can implement some nice interactive features, such like a dynamic presentation of the links to further pages of that site.

So, recently I developed a dynamic, rotating menu where the links to the different pages of a website are revolving in a circle as graphical symbols and where the user is able to affect the speed of the rotation as well as its direction and the tilt of the circle by his mouse:

 

 

The graphical layout

For this ActionScript exercise we will only need one single frame in one layer. However, as the first step we have to create the graphical symbols: I used the 3D animation software blender in order to create those symbols that will represent the links, but you can also use other graphic programs or even your Flash editor itself. In case you are using a certain graphic program you should build your images that way that the symbols fit well in the size of the image, just to avoid a too big overlap between those symbols. In this example I created 6 different symbols (in a PNG 24 format) representing the links to the single pages of a website (for demonstration purposes, I only used the symbol „Home“ as a real link to another page of my site here).

In the next step, you will have to import those symbols in your Flash editor: Just drag your image files to the editor and drop them somewhere on the stage. Now you have to convert each of those symbols into a MovieClip object and give it a certain instance name (I simply used the symbol words as instance names, i.e., „Home“, „Forum“, etc.).

As a further part of our application we need another MovieClip object that will act as the control instance (see below): I just added a black bar to the bottom of the stage, converted it into a MovieClip and gave it the instance name „Control“. However, this object will not be visible in the final version of our application (see Fig. 1).

 

Figure 1.

 

The main ActionScript code

In this section we will develop the main ActionScript code for our appliciation, namely the definition of some global variables, as well as our core function that will be responsible for the motion of our symbols.

 

1) The definition of the global variables

We need seven global variables for our applicition as defined in the following code block:

 


_global.clips = [[Home,0],[Forum,60],[Server,120],[Joinus,180],[Gbook,240],[Rules,300]];
_global.angleIncrement = 1;
_global.ellipseTilt = 0.0;
_global.radius = 100;
_global.centerScreen = [260,150];
_global.ellipseScaleX = 1.8;
_global.rotFlag = true;

 

The first variable _global.clips represents an array that consists of further sub-arrays. Each of this sub-arrays has two elements: The instance name of our symbols and a number that represents the position of that symbol on the rotating circle, expressed as degree. I used equidistant steps for the positions of my six symbols (i.e. 60 degree steps), but it's entirely up to you, so you can use any degree value you like.

The next three variables represent the increment of the angle for the next frame (_global.angleIncrement) that correlates with the rotation speed of our circle (the higher that value the faster the rotation will be), the initial value for the tilt of the circle (_global.ellipseTilt) and the radius of the circle expressed in pixels (_global.radius).

The following variable (_global.centerScreen) is an array that represents the center point of our screen, i.e., the x- and the y-coordinate of this center point. Since I set the width of the screen to 520 pixels and the height of it to 300 pixels, the two elements of this array have the values 260 and 150. The next variable (_global.ellipseScaleX) represents a scaling factor for the x-axis of our circle, that means, the resulting motion guide line will rather have the shape of an ellipse, unless this value is set to 1.0. In this example the x-axis is stretched by the value 1.8. And as a last global variable we have a Boolean one (_global.rotFlag) that will play a role for the handling of the mouse events (this will be discussed in detail below).

 

2) The core function

The core function spin(mc_array, incr, tilt) of our application contains many steps that will be explained in detail. For a better overview I split the entire code into eight separate sections (a-h) and added a brief description to each of it as a remark:

 


function spin(mc_array:Array, incr:Number, tilt:Number):Void
{	
	for(var i = 0; i < mc_array.length; i++)
	{
		///(a) Extract the name of the MovieClip and its position on the circle
		var name:MovieClip = mc_array[i][0];
		var angle:Number = mc_array[i][1];
		
		///(b) Transform the angle into the x- and y-coordinate
		var rad:Number = ((angle)/180.0)*Math.PI;
		var x:Number = Math.sin(rad);
		var y:Number = Math.cos(rad);
		
		///(c) Adjust the center point of the MovieClip, which is originally located at the upper left corner
		var width:Number = name._width;
		var height:Number = name._height;		
		
		///(d) Set the position of the MovieClips
		name._x = _global.centerScreen[0]+_global.ellipseScaleX*_global.radius*x-width/2;
		name._y = _global.centerScreen[1]+_global.radius*y*tilt-height/2;//
		
		///(e) Adjust the depth layer of the MovieClips
		name.swapDepths(Math.round(y*1000));
		
		///(f) Create a pseudo-perspective scaling (dependent on the y-value)
		name._xscale = 70+((y+1)/2)*30;
		name._yscale = 70+((y+1)/2)*30;
		
		///(g) Apply transparency to the MovieClips (dependent on the depth value)
		name._alpha = 100-((y+1)/2)*15;
	
		///(h) Increment angle
		_global.clips[i][1] = (_global.clips[i][1] + incr)%360;
	
	}	
}

 

The function requires 3 arguments: An array that contains the instance names as well as the current circle location (in degree) of the entire set of graphical symbols, a number that represents the increment for the location of the symbol on the circle (in degree), and a number that represents the tilt of the circle/ellipse towards the line of sight (in pixels). Each element (=symbol) of the array will pass through the same procedure, using a for-loop:

a) In a first step, the instance name and the current circle position of the specific MovieClip object (symbol that represents the menu link) is extracted from the array.

b) In the next code block this angle will first be converted from degree into radians, since the latter is the usual input format of an angle as it is used for trigonometric functions. This value can than be used for the calculation of the x- and y-coordinates of the symbols, by means of the sine and cosine functions.

c) This step is only required in case that the “pivot” of the graphical symbol is not located at the center of the symbol, but instead in the upper left corner (which is the case here). The width and the height of the symbol are extracted in order to adjust the center point in a subsequent step:

d) In this code block, the single graphical symbols receive their exact x-coordinate (the horizontal position on the screen) and y-coordinate (the vertical position), relative to center of the screen (expressed in the array _global.centerScreen). For the calculation of the x-coordinate, we virtually construct an ellipse with its center in the middle of the screen, using the radius that was defined before in a global variable, as well as the stretching variable _global.ellipseScaleX (the y-axis, however, will be constant with a factor of 1.0). For the y-coordinate, we take the tilt-argument into account, that is used to shift the vertical position of the symbols relative to the center of the screen.

e) This is a very important step: Just leave it out for a moment and see what happens! Since we have a dynamic, rotating presentation of graphical symbols, one symbol will occasionally be occluded by another – and actually, one symbol will occlude another symbol in one frame, and in a different frame, it will be occluded itself by exactly the same symbol! Since occlusion is a feature that is controlled by the depth layer specification in ActionScript, we have to adjust this specification for each symbol in each frame, because the (virtual) depth location of each symbol is constantly changing during the rotation. So, what happens in this code line is the adjustment of the depth layer of the MovieClip object, using the method swapDepths(). As argument, we simply pass the cosine value from step b, that is stored in the local variable y, and since variable y is a floating point number within the interval (0, 1), we will produce integer values between 0 and 1000 in this code here. This way, the depth layer for each symbol will be updated with every new frame.

f) We want to evoke the perception of depth with our rotating menu: In the former step, we already used one visual depth cue that produced the idea that one object is located behind another – by means of occlusion. Here, we will use another depth cue: Perspective! We all know that the image of an object on the retina will be smaller the further away it is from the observer. Hence, in this step we adjust the size of each symbol, dependent on our local variable y (cosine of the circular location). Actually, what we will do here is just a “pseudo-perspective” projection, since for a real perspective projection we would have to define something like the “visual cone” (or better: the visual frustum), which would only make sense if we have information about some further geometrical factors. Instead, we will use an orthographic projection and simulate a perspective one by means of a scaling of the size of the image, for which I used arbitrary basic values: If the symbol is located in the furthest depth position (i.e., when the cosine of the angle of the symbol equals -1.0), the image size would be 70% of its original size (you can try out different values for it if you like). Between the furthest and the nearest depth position, a continuous adjustment of the size will take place which each frame.

g) As an extra feature, I adjusted the transparency of the graphical symbols in this step, using a similar procedure as in the former step: Each symbol has a minimum opacity of 85%, however, the further away it is, the more opaque it will be. That means that symbols that appear closer to the observer will be more transparent and allow a clearer view through them to symbols that will be occluded by them. If you don't like that feature, you can simply leave that step out.

h) In the last step, we update the position of the symbol on the circle, i.e., we add the increment value incr to the current angular value of the symbol (as it is stored in the second element of each sub-array of the global array _global.clips). So this is the step that is responsible for the rotation of the menu.

 

The ActionScript code for the single MovieClips

In this section the code for the single graphical symbols will be explained: We will connect each symbol to 3 MovieClip events: In case the mouse cursor rolls over one of the symbols of the menu, the rotation will stop, so that the user will not be forced to “hunt” for the link he wants to click. In case that the mouse cursor rolls out, the rotation will proceed. And last but not least: If the user clicks on the symbol, the linked page shall be opened. So, for each of our 6 example symbols in the present exercise, we have to add the following code in the respective ActionScript sheets (just click on the single symbol on the stage of your Flash editor to get to its ActionScript window).

 


on(rollOver)
{
	_global.rotFlag = false;
}

on(rollOut)
{
	_global.rotFlag = true;
}

on(press)
{
	getURL("./index.php", "_self");
}

 

For the first two MovieClip events (rollOver and rollOut), simply the Boolean variable _global.rotFlag will be updated. For the remaining MovieClip event (press) we use the function getURL() in order to produce a hyperlink. We will pass two values as arguments to this function: The first is the respective file name of the page that shall be opened (note that you have to exchange that link), and the second tells the function to open the new page in the same browser window (if you want to open that linked page in a new window you have to use the keyword “_blank” instead).

 

The ActionScript code for the control element

We're almost there! If you start your application in its present incarnation in the preview window, the links will already work, however: nothing is rotating—you have just a composition of symbolic links at their original, static locations. So, let's put some life in our application: The call of our core function spin() will be performed by the MovieClip object with the instance name Control:

 


onClipEvent(enterFrame)
{
	_level0.spin(_global.clips, _global.angleIncrement, _global.ellipseTilt);
	
}

onClipEvent(mouseMove)
{
	var rel_x:Number =_global.centerScreen[0] - _level0._xmouse;
	var rel_y:Number =_global.centerScreen[1] - _level0._ymouse;
	
	if(_global.rotFlag)
		_global.angleIncrement = -rel_x/300;
	else
		_global.angleIncrement = 0;	
		
	_global.ellipseTilt = rel_y/1000;
}

 

Each time the program enters a new frame, this MovieClip object will be called by means of the MovieClip event enterFrame. Additionally, this Control element will handle the mouse event mouseMove, as it is defined in the next code block. As soon as the mouse cursor is moved, the following code will be executed: First, the location of the mouse cursor relative to the center of the window will be determined. If the mouse cursor covers one of the symbols, i.e., if the variable rotFlag is false, the variable _global.increment will receive the value 0 (the rotation will stop). Otherwise, this variable will receive a value for the rotation speed that depends on the relative x-position of the mouse cursor. This value controls the direction of the rotation as well as the speed of it: The more eccentric this mouse position is, the faster the rotation will be. In a similar way we define the value for the variable _global.ellipseTilt that depends on the eccentricity of the y-position of the mouse cursor and that is responsible for the tilt of the ellipse. The two adjusting values (300 or 1000, respectively) I used for these two variables are quite arbitrary—you may use different ones instead in order to determine a different basis for the rotation speed and the tilt of the ellipse. That's it!

 

 

© 2011 G. Wendt



Write a comment
Name: Note: Your email address will not be displayed on this site nor otherwise published!
Email:
Comment:
Anti-Spam:
Color & Font
Color tool

You have difficulties finding a balanced color combination and appropriate font formats for your website?


Use the tool Color & Font on this website in order to interactively create the color and font properties of your own site. The results of your settings can be easily included as CSS listing in your web project.

Archive
MTWTFSS
1234567
891011121314
15161718192021
22232425262728
2930