Tutorials: Step by Step to Create your Sector Menu

Is the sector menu following a cool stuff? I am sure you won’t provide me a negative answer. Thank you!

[PieMenu.swf]

clip_image002 

Search-256x256 Demo | DownloadDownload Full Project

OK, let’s start to build this cool stuff now.

By specifying the start position and the angle, we will get a variety of sectors.

clip_image004

clip_image006

clip_image008

This means that the start position and the angle is arbitrary , to draw a sector.

1. Draw sector.

The key point to build the menu is to draw sector and to generate the transition animation. Obviously, the menu consists of sectors. The process of the menu generation is the process of drawing sectors and performing animations. So, let’s draw a sector at first.

The sector may start at uncertain position and end with arbitrary angle. So, we suspend that the start position is -30 degree, and the angle is -105 degree.

[tween_with_angle.swf]

clip_image010

The sector in the image above is started at -30 degree, end at -135 degree, the angle is -105 degree, the inner radius is 50, the outer is 100, the coordinate of the centre circle is (x,y).

The edges of the sector consist of two lines and to arcs. Anticlockwise, they are line p1p2, arc p2p3, and line p3p4, arc p4p1. What we should do is just drawing the edges and filling the blank area.

We calculate the coordinates of the four points, p1, p2, p3 and p4 first of all.

P1: (x+r*cos(start),y+r*sin(start))
P2:(x+R*cos(start),y+R*sin(start))
P3:(x+r*cos(start+angle),y+r*sin(start+angle))
P4:(x+R*cos(start+angle),y+R*sin(start+angle))

The next problem is the algorithm to draw arcs. As we know, we can draw arc if the centre angle is small than 45 degree. So we divide the centre angle of a large arc equally into parts whose centre angle are small than 45 degree, then, draw the small arcs and combine them together to form the large one.

There exists mature codes create by Geordi (tell me if you know whom he is). The function “DrawSector” has been modified as below:

//”angle” is the value of centre angle, “startA” is the start position.
//to gets the number of the divided parts with 45 degree centre angle.
var n:Number = Math.ceil(Math.abs(angle) / 45);

//to gets the exact centre angle of every part.
var angleA:Number=angle / n;

angleA = angleA * Math.PI / 180;
startA = startA * Math.PI / 180

// loop to draw the arcs.
for (var i=1; i <= n; i++)
{
startA += angleA;

var angleMid1:Number=startA - angleA / 2;
var bx:Number = x + R / Math.cos(angleA / 2) * Math.cos(angleMid1);
var by:Number = y + R / Math.cos(angleA / 2) * Math.sin(angleMid1);
var cx:Number = x + R * Math.cos(startA);
var cy:Number = y + R * Math.sin(startA);

sector.graphics.curveTo(bx, by, cx, cy);
}

If it’s hard to understand, try to learn from the picture below:

clip_image012

Tangent point P is the reference point of the method “curveTo”.

OK, theory is all. Let’s create a new fla file in flash cs3, write the codes below at the action panel of the first frame:

var sector:Sprite = new Sprite();

addChild(sector);

drawSector(200, 200, 50, 100, -105, -30);

function drawSector( x:Number, y:Number, r:Number, R:Number, angle:Number, startA:Number)
{
sector.graphics.clear();
sector.graphics.lineStyle (1, 0, 1, true);
sector.graphics.beginFill(0, 0.5);

if (Math.abs(angle) > 360)
{
angle=360;
}

var n:Number = Math.ceil(Math.abs(angle) / 45);
var angleA:Number=angle / n;

angleA = angleA * Math.PI / 180;
startA = startA * Math.PI / 180;

var startB:Number = startA;

//start edge
sector.graphics.moveTo(x + r * Math.cos(startA), y + r * Math.sin(startA));
sector.graphics.lineTo(x + R * Math.cos(startA), y + R * Math.sin(startA));

//outer arc
for (var i=1; i <= n; i++)
{
startA += angleA;

var angleMid1:Number=startA - angleA / 2;
var bx:Number = x + R / Math.cos(angleA / 2) * Math.cos(angleMid1);
var by:Number = y + R / Math.cos(angleA / 2) * Math.sin(angleMid1);
var cx:Number = x + R * Math.cos(startA);
var cy:Number = y + R * Math.sin(startA);

sector.graphics.curveTo(bx, by, cx, cy);
}

// start position of inner arc
sector.graphics.lineTo(x + r * Math.cos(startA),y + r * Math.sin(startA));

//inner arc
for (var j = n; j >= 1; j–)
{
startA-= angleA;

var angleMid2:Number=startA + angleA / 2;
var bx2:Number=x + r / Math.cos(angleA / 2) * Math.cos(angleMid2);
var by2:Number=y + r / Math.cos(angleA / 2) * Math.sin(angleMid2);
var cx2:Number=x + r * Math.cos(startA);
var cy2:Number=y + r * Math.sin(startA);

sector.graphics.curveTo(bx2, by2, cx2, cy2);
}

// end position of inner arc.
sector.graphics.lineTo(x + r * Math.cos(startB),y + r * Math.sin(startB));

//done
sector.graphics.endFill();
}

Now, press CTRL+ENTER to test the movie. (The center point of the coordinates system is the point (200,200))

[draw_sector.swf]

clip_image014

Now, we have finished drawing a sector.

2. Generate transition animation.

Secondly, let’s start to generate the transition animation.

With the codes above, we know that to achieve the effect “opening and rotating” what we should do is just changing the start position and the centre angle.

The “opening” effect is just changing centre angle with fixed start position. Take the case of the previous data, the start position is -30 degree, centre angle is from 0 to -105 degree.

Appending the codes below to see that the centre angle is being changed:

import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.Strong;

var obj:Object = {};

obj.angle = 0;

var tween:Tween=new Tween(obj, "angle", Strong.easeInOut, 0, -105, 5, true);

tween.addEventListener(TweenEvent.MOTION_CHANGE, changeHandler);

function changeHandler(event:TweenEvent):void
{
drawSector(200, 200, 50, 100, obj.angle, -30);
}

The codes above make use of built-in class Tween, which class TweenMax will substitute as.

CTRL+ENTER to do test.

[tween_with_angle.swf]

clip_image016

OK, next is changing the start position. Let’s suspend the centre angle is 105 degree; the start position is 75 degree; rotate to -30 degree to stop.

Modify the codes above a littler to see the changing start position:

import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.Strong;

var obj:Object = {};

obj.start = 0;

var tween:Tween=new Tween(obj, "start", Strong.easeInOut, 75, -30, 5, true);

tween.addEventListener(TweenEvent.MOTION_CHANGE, changeHandler);

function changeHandler(event:TweenEvent):void
{
drawSector(200, 200, 50, 100, -105, obj.start);
}

CTRL+ENTER to do test.

[tween_with_start.swf]

clip_image018

With the two processes above, we deduce the generation of the sub-menu: the start position is fixed at first, the changing one is centre angle. When the centre angle increases at the target value, start to change the start position, the centre angle is fixed now and the arc is rotating around the center. The generation stops when the start position is at the target value.

However, there remains a problem: How to generate fluent animation if we start the “opening” effect at first then the “rotating” effect? We analyze this in another way.

We suspend the centre angle is a positive. So, the start edge should be counter-clockwise to the end edge. Rotating the start edge with “angle” anticlockwise, the centre angle will be updated as “angle”. It looks like that the end edge is fixed; the centre angle is increasing. When the centre angle is at the target value, fix the centre angle, go on rotating the start edge and stop at the target value. This is the whole animation process.

According to the analysis above, we code as below:

import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.Strong;

var obj:Object = {};
obj.start = 0;
obj.angle = 0;
var tween:Tween;

//begin: the initial value of start position
// end: the last value of end position
// angle: the abstract value of centre angle
function create (begin:Number, end:Number, angle:Number, tweenTime:Number):void
{
var oldStart:Number=0;

tween=new Tween(obj, "start", Strong.easeInOut, begin, end, tweenTime, true);
tween.addEventListener(TweenEvent.MOTION_CHANGE, changeHandler);

function changeHandler(event:TweenEvent):void
{
if (Math.abs(obj.angle) >= angle)
{
drawSector(200, 200, 50, 100, angle, obj.start);
}
else
{
obj.angle = Math.abs(oldStart - obj.start)
drawSector(200, 200, 50, 100, obj.angle, obj.start);
}
}
}

//from 0 degree, start edge rotates to -135 degree, the duration is 5 seconds.
create (0, -135, 105, 5);

CTRL+ENTER to do test.

[tween_without_filters.swf]

clip_image020

3. Decorate sector.

OK, the left thing is to add suitable filters to decorate the menu and make it beautiful. We will take advantage of BevelFilter and DropShadowFilter to make the menu stereoscopic with two appended methods:

function getBevelFilter():BitmapFilter
{
var distance:Number = 6;
var angleInDegrees:Number = 45;
var highlightColor:Number = 0xFFFFFF;
var highlightAlpha:Number = 0.6;
var shadowColor:Number = 0xFFFFFF;
var shadowAlpha:Number = 0;
var blurX:Number = 10;
var blurY:Number = 10;
var strength:Number = 0.8;
var quality:Number = BitmapFilterQuality.LOW;
var type:String = BitmapFilterType.INNER;

var knockout:Boolean = false;

return new BevelFilter(distance,angleInDegrees,highlightColor,highlightAlpha,shadowColor,shadowAlpha,blurX,blurY,strength,quality,type,knockout);
}

function getDropShadowFilter():BitmapFilter
{
var color:Number = 0×000000;
var angle:Number = 45;
var alpha:Number = 0.9;
var blurX:Number = 5;
var blurY:Number = 5;
var distance:Number = 5;
var strength:Number = 0.9;
var inner:Boolean = false;
var knockout:Boolean = false;
var quality:Number = BitmapFilterQuality.LOW;

return new DropShadowFilter(distance,angle,color,alpha,blurX,blurY,strength,quality,inner,knockout);
}

Then, set the filter to sector.

sector.filters = [getBevelFilter(), getDropShadowFilter()];

Substitute the filling color in the method “create” above with a more abstract one.

sector.graphics.beginFill(0×0066FF, 0.8);

CTRL+ENTER to do test.

[tween_with_filters.swf]

clip_image022

4. Build menu.

To create a real menu, we should specify the range of start position and the centre angle of each sub-menu according to the total angle and the sub-menu number, and then start up the “create” method of each sub-menu. I have coded this part; everyone will get all the source codes and examples at the bottom of this article.

If this menu is used in flex, the class “Tween” in flash cs3 should substitute as the “Tween” in flex.

Pseudo codes looks like the below:

import mx.effects.Tween;
import mx.effects.easing.*;

private var tween:Tween;

public function create (value:Number ,delta:Number, tweenTime:Number):void
{
//…
var listener:Object = {};

listener.onTweenUpdate = function(val:Object):void
{
// update the centre angle
}

listener.onTweenEnd = function(val:Object):void
{
//
}

tween = new Tween(listener, obj.start, value, tweenTime*1000);
tween.easingFunction = Quadratic.easeInOut;
}

Now, it can be used in flex.

OK, that’s all. Hope these words are useful.

Cheers!

[Linkage of download files]

All the related contents will be found here and all images.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • Reddit
  • Technorati
  • StumbleUpon
  • Twitter
RSS Enjoy this Post? Subscribe to Ntt.cc

RSS Feed   RSS Feed     Email Feed  Email Feed Follow us Follow us
You can leave a response, or trackback from your own site.

10 Responses to “Tutorials: Step by Step to Create your Sector Menu”

  1. Pretty Cool Menu.

    And Thanks a lot for sharing this tutorial. You have given us a reason to brush up our basics in Trigonometry.

  2. sam says:

    Rapid clicking breaks it

  3. As Sam already said, rapid clicking on the middle button breaks the menu. The menu then does not go back to its origin state (only the middle button), but displays always the fully expanded menu. Also the memory consumptions seems to grow way to high and fast.

    Without having investigated this bug too much, I would suggest to introduce a state variable that checks. This variable is set to EXPANDED when the user clicks on the middle button and prevents the user from clicking on the menu too quickly.

  4. [...] 接下来的例子演示了Flex中利用showDataEffect效果和SeriesInterpolate类,创建动态效果图表(chart)。类似的动态效果(非图表),可以看一篇更加具体的教程:Tutorials: Step by Step to Create your Sector Menu. [...]

  5. [...] 后 ,auzn又出品了经典作品–扇形菜单。本文英文版:Tutorials: Step by Step to Create your Sector Menu。 [...]

  6. mnieves says:

    a Flex project example please!
    how to compile the this project as swc to use it in flex.
    thanks!

  7. Nadia says:

    I cannot download these files.Why?

  8. Ntt.cc says:

    @Nadia
    You must download it from browse directly, if use other tools like FlashGet or Xunlei, maybe you will get 404 error.

  9. Adrien says:

    Hello,

    thanks to share it. I’ve done a Flex version of this, it works but I’m still having some issues.

    The major one is that we can’t add Text as you did in Flash, and the position is not good at all.

    If someone wants to see it, here are the files : http://www.megaupload.com/?d=8M7VJMOI

    Just need to create an instance of Sector Menu.

Leave a Reply