RECAP:
Well, so far, in Part I, we put together a background for our website and created a "main display" graphic which will (eventually) display our site's content and that stays centered on our web page whenever the browser window is resized. In this segment, we'll start adding some functionality to our site by creating a menu. In the process we'll see a few new features of Actionscript 3 (for example, the new SimpleButton class and the ability to have multiple classes in a single .as file) and take a closer look at some more advanced event handling. We'll also take a look at working with sounds in AS3 to give our menu buttons a little click in the pants (yeah, that was bad).
A MENU:
Let's jump right in by creating our Menu class file. Inside your "iface" directory (as we're now creating what we'll consider an interface item), create a new .as file named "Menu.as". Once again, this class will extend Flash's Sprite class, so we can quickly create the class definition and constructor method like so:
package classes.iface {
import flash.display.Sprite;
public class Menu extends Sprite {
public function Menu() {
init();
}
private function init():void {
trace ("Menu instance created!");
}
}
}
All that is pretty much business as usual. Now, though, we're going to try something completely new. Inside this same .as file, we will define a "helper" class which will create a single menu button to add to our menu. When creating additional classes inside of .as files in this manner, the helper class must be totally outside of the main class being defined in the .as file. This means that the class will be outside of the package and will only be accessible to the main class.
This helper class, in particular, will extend the SimpleButton class. The SimpleButton class is new to Actionscript 3. In essence, it allows the Flash developer to programmatically create instances of Button symbols complete with up, over, down, and hit states. Basically, those states are created by assigning 3 different Sprite instances to the appropriate properties of the SimpleButton. That tells us immediately that our helper class, which we will define as MenuButton, will need to import the Sprite, and SimpleButton classes. We'll also want to create a nice gradient as well as use a few filters on our menu buttons, so we'll import those as well. Finally, we will also need a label on our buttons, so we'll import some classes from the flash.text package as well. All together our import list will look like this:
import flash.display.Sprite; import flash.display.SimpleButton; import flash.display.GradientType; import flash.display.SpreadMethod; import flash.geom.Matrix; import flash.filters.DropShadowFilter; import flash.filters.BevelFilter; import flash.filters.GlowFilter; import flash.text.TextField; import flash.text.TextFormat; import flash.text.TextFormatAlign; import flash.text.AntiAliasType;
If that seems like an inordinate number of imports for creating just a "simple" button, well, get used to it. You'll be seeing a great deal of that in Actionscript 3. The (private) properties we'll want our button to have are: _upShadow (a DropShadowFilter for the up state of our button), _downShadow (a DropShadowFilter for the down state), _overGlow (a GlowFilter for the button's over state), _bevel (a BevelFilter for a all states of our button), _btnText (a String which will be our button's label), _color (a uint hexidecimal color), _idNum, and _newColor (we'll see later how these last two properties will be used).
The properties of the SimpleButton class we wish to define are upState, overState, downState, and hitTestState. Each property will accept an instance of the Sprite class as a value. The hitTestState (much like the "hit" frame of a Button symbol) will never be seen, but instead describes what part of the SimpleButton will react to mouse activity. We can easily just use our upState for our hitTestState. We will also need to create a text field for our button labels. Text fields are fairly self explanatory in Actionscript 3 as there is very little change from AS2. There are a couple main ways to apply a format to a textfield. If your textfield is already displaying text, you can use the TextField.setTextFormat() method. To apply a format to a textfield that has no set text property, you can use the TextField.defaultTextFormat property, as we'll be doing here. Both of these approaches use a TextFormat instance as an argument. To get a look at the parameters we will be passing to our TextFormat instance you can check out the documentation here. The approach we'll be taking in our button creation will be to create methods that draw each of our button's states and another method to add the textfield label. Because we've already seen a few example of the graphics drawing api hard at work, we'll just take a look at the final product here:
// imports for internal MenuButton class
import flash.display.Sprite;
import flash.display.SimpleButton;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.geom.Matrix;
import flash.filters.DropShadowFilter;
import flash.filters.BevelFilter;
import flash.filters.GlowFilter;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.text.AntiAliasType;
internal class MenuButton extends SimpleButton {
private var _upShadow:DropShadowFilter;
private var _downShadow:DropShadowFilter;
private var _overGlow:GlowFilter;
private var _bevel:BevelFilter;
private var _color:uint;
private var _idNum:int;
private var _btnText:String;
private var _newColor:uint;
function MenuButton(nc:uint, btnText:String, id:int) {
_newColor = nc;
_btnText = btnText;
_idNum = id;
_color = 0x669900;
_bevel = new BevelFilter(4, 90, 0xFFFFFF, .8, 0x000000, .1, 0, 3, 1, 3);
_upShadow = new DropShadowFilter(3, 90, 0x000000, .6, 4, 4, 1, 3);
_downShadow = new DropShadowFilter(1, 90, 0x000000, .8, 2, 2, 1, 3);
_overGlow = new GlowFilter(0xFFFF66, .5, 10, 10, 1, 3, true);
_fmt = new TextFormat("Arial", 11, 0x000000, null, null, null, null, null, TextFormatAlign.CENTER);
drawButton();
}
public function drawButton():void {
this.upState = drawUp();
this.overState = drawOver();
this.downState = drawDown();
this.hitTestState = drawUp();
}
private function drawUp():Sprite{
var cols:Array = [_color, 0x222222];
var m:Matrix = new Matrix();
m.createGradientBox(100, 25, 90*Math.PI/180);
var s:Sprite = new Sprite();
s.graphics.beginGradientFill(GradientType.LINEAR, cols, [1, 1], [100, 255], m, SpreadMethod.PAD);
s.graphics.drawRoundRect(0, 0, 100, 20, 20, 20);
s.graphics.endFill();
var tf:TextField = buttonText();
s.addChild(tf);
s.filters = [_bevel, _upShadow];
return s;
}
private function drawDown():Sprite {
var cols:Array = [_color, 0x222222];
var m:Matrix = new Matrix();
m.createGradientBox(100, 25, 90*Math.PI/180);
var s:Sprite = new Sprite();
s.graphics.beginGradientFill(GradientType.LINEAR, cols, [1, 1], [100, 255], m, SpreadMethod.PAD);
s.graphics.drawRoundRect(0, 2, 100, 20, 20, 20);
s.graphics.endFill();
var tf:TextField = buttonText();
tf.y = 2;
s.addChild(tf);
s.filters = [_bevel, _downShadow];
return s;
}
private function drawOver():Sprite {
var cols:Array = [_color , 0x222222];
var m:Matrix = new Matrix();
m.createGradientBox(100, 25, 90*Math.PI/180);
var s:Sprite = new Sprite();
s.graphics.beginGradientFill(GradientType.LINEAR, cols, [1, 1], [100, 255], m, SpreadMethod.PAD);
s.graphics.drawRoundRect(0, 0, 100, 20, 20, 20);
s.graphics.endFill();
var tf:TextField = buttonText();
s.addChild(tf);
s.filters = [_overGlow, _bevel, _upShadow];
return s;
}
private function buttonText():TextField {
var fmt = new TextFormat("Tahoma", 10, 0x000000, null, null, null, null, null, TextFormatAlign.CENTER);
var tf:TextField = new TextField();
tf.width = 100;
tf.height = 20;
tf.antiAliasType = AntiAliasType.ADVANCED;
tf.selectable = false;
tf.defaultTextFormat = fmt;
tf.text = _btnText;
return tf;
}
}
A quick interjection here. As briefly stated before, when adding additional classes to .as files, those extra classes are only accessible by the main class of that .as file. Therefore, declaring the class "internal", as we've done here, is really unnecessary and a bit on the redundant side (the internal keyword specifies that a class or method is available only to other classes within the same package). It does, however, make for clear, well-written code, so we'll go ahead and leave it in. Bad shortcuts will develop in time.
FROM BUTTON TO MENU
Now, that we have our MenuButton class written, let's go back and see how we can add those buttons to our Menu. Our Menu class will need an Array property to hold our button instances. We'll also want arrays to hold the labels for our buttons and the new colors (we'll get back to that color thing in time, just go with it for now). We'll also want a Number that specifies how far apart our buttons appear. Finally, since we are dealing with buttons after all, we'll want to import the MouseEvent class to handle the button clicks. The general idea here will be to loop through our array of button labels and for each label, we will create a new MenuButton, position that button according to our button spacing number, add the button to the display list (so that it will be seen), push that button into our array of button instances, and finally add an event listener that will will listen for button clicks and call a method named "onClick" which will dispatch that button click to another event listener (we'll come back to this point in a bit as it will get a tad tricky). This, though, is our complete Menu.as file (at least for now):
package classes.iface {
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Menu extends Sprite {
private var _btns:Array;
private var _btnLabels:Array;
private var _btnColors:Array;
private var _btnSpacing:Number;
public function Menu() {
_btns = new Array();
_btnSpacing = 120;
_btnLabels = new Array ("Home", "Product", "Contact");
_btnColors = new Array(0x669900, 0xCC0066, 0x6699FF);
init();
}
private function init():void {
var numButtons:int = _btnLabels.length;
for (var i = 0; i < numButtons; i++) {
var b:MenuButton = new MenuButton(_btnColors[i], _btnLabels[i], i);
b.x = i * _btnSpacing;
addChild(b);
_btns.push(b);
b.addEventListener(MouseEvent.CLICK, onClick);
}
}
private function onClick(e:MouseEvent):void {
trace ("you clicked a menu button.");
}
}
}
// imports for internal MenuButton class
import flash.display.Sprite;
import flash.display.SimpleButton;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.geom.Matrix;
import flash.filters.DropShadowFilter;
import flash.filters.BevelFilter;
import flash.filters.GlowFilter;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.text.AntiAliasType;
internal class MenuButton extends SimpleButton {
private var _upShadow:DropShadowFilter;
private var _downShadow:DropShadowFilter;
private var _overGlow:GlowFilter;
private var _bevel:BevelFilter;
private var _color:uint;
private var _idNum:int;
private var _btnText:String;
private var _newColor:uint;
public function MenuButton(nc:uint, btnText:String, id:int) {
_newColor = nc;
_btnText = btnText;
_idNum = id;
_color = 0x669900;
_bevel = new BevelFilter(4, 90, 0xFFFFFF, .8, 0x000000, .1, 0, 3, 1, 3);
_upShadow = new DropShadowFilter(3, 90, 0x000000, .6, 4, 4, 1, 3);
_downShadow = new DropShadowFilter(1, 90, 0x000000, .8, 2, 2, 1, 3);
_overGlow = new GlowFilter(0xFFFF66, .5, 10, 10, 1, 3, true);
_fmt = new TextFormat("Arial", 11, 0x000000, null, null, null, null, null, TextFormatAlign.CENTER);
drawButton();
}
public function drawButton():void {
this.upState = drawUp();
this.overState = drawOver();
this.downState = drawDown();
this.hitTestState = drawUp();
}
private function drawUp():Sprite{
var cols:Array = [_color, 0x222222];
var m:Matrix = new Matrix();
m.createGradientBox(100, 25, 90*Math.PI/180);
var s:Sprite = new Sprite();
s.graphics.beginGradientFill(GradientType.LINEAR, cols, [1, 1], [100, 255], m, SpreadMethod.PAD);
s.graphics.drawRoundRect(0, 0, 100, 20, 20, 20);
s.graphics.endFill();
var tf:TextField = buttonText();
s.addChild(tf);
s.filters = [_bevel, _upShadow];
return s;
}
private function drawDown():Sprite {
var cols:Array = [_color, 0x222222];
var m:Matrix = new Matrix();
m.createGradientBox(100, 25, 90*Math.PI/180);
var s:Sprite = new Sprite();
s.graphics.beginGradientFill(GradientType.LINEAR, cols, [1, 1], [100, 255], m, SpreadMethod.PAD);
s.graphics.drawRoundRect(0, 2, 100, 20, 20, 20);
s.graphics.endFill();
var tf:TextField = buttonText();
tf.y = 2;
s.addChild(tf);
s.filters = [_bevel, _downShadow];
return s;
}
private function drawOver():Sprite {
var cols:Array = [_color , 0x222222];
var m:Matrix = new Matrix();
m.createGradientBox(100, 25, 90*Math.PI/180);
var s:Sprite = new Sprite();
s.graphics.beginGradientFill(GradientType.LINEAR, cols, [1, 1], [100, 255], m, SpreadMethod.PAD);
s.graphics.drawRoundRect(0, 0, 100, 20, 20, 20);
s.graphics.endFill();
var tf:TextField = buttonText();
s.addChild(tf);
s.filters = [_overGlow, _bevel, _upShadow];
return s;
}
private function buttonText():TextField {
var fmt = new TextFormat("Tahoma", 10, 0x000000, null, null, null, null, null, TextFormatAlign.CENTER);
var tf:TextField = new TextField();
tf.width = 100;
tf.height = 20;
tf.antiAliasType = AntiAliasType.ADVANCED;
tf.selectable = false;
tf.defaultTextFormat = fmt;
tf.text = _btnText;
return tf;
}
}
SEEING IS BELIEVING
Whew. That seems like a lot of work without really knowing how it's looking, so let's go back to our WebSite class and take a peak at what's going on. In WebSite.as, let's add a new method to add our Menu to our website's display list. First, of course, we'll need to import our classes.iface.Menu class and create a private _menu property. Then it's just a matter of adding a simple quick method (similar to our addBackground() methodd to add the Menu to our site. So, for now, this will be our entire WebSite class:
package classes {
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.filters.BevelFilter;
import flash.filters.DropShadowFilter;
import classes.graphics.*;
import classes.iface.Menu;
public class WebSite extends Sprite {
private var _bg:Background;
private var _mainDisplay:MainDisplay;
private var _menu:Menu;
public function WebSite() {
init();
}
private function init():void {
initStage();
addBackground();
addMenu();
addMainDisplay();
onStageResize();
}
private function initStage():void {
stage.frameRate = 31;
stage.showDefaultContextMenu = false;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.addEventListener(Event.RESIZE, onStageResize);
}
private function onStageResize(... eventArray:Array):void {
_bg.resize(stage.stageWidth, stage.stageHeight);
_mainDisplay.reposition(stage.stageWidth / 2 - _mainDisplay.width / 2, stage.stageHeight / 2 - _mainDisplay.height / 2);
}
private function addBackground():void {
_bg = new Background();
addChild(_bg);
}
private function addMainDisplay():void {
_mainDisplay = new MainDisplay();
_mainDisplay.filters = [new BevelFilter(3, 90, 0xFFFFFF, .8, 0x000000, .65, 3, 3, 1, 3), new DropShadowFilter(8, 90, 0, .75, 7, 7, 1, 3)];
addChild(_mainDisplay);
}
private function addMenu():void {
_menu = new Menu();
addChild(_menu);
}
}
}
Notice that we add the menu to the display list before we add the main display. This is just so that if there is any overlap at all, the main display will appear to be above the menu. Now, go ahead and test your movie. And there, currently stuck in the upper left corner, is our menu. Go ahead and play with it. Roll over the buttons, click on them and notice how their states change and we get a trace telling us that a button was clicked. Not too shabby. Still, we don't really want the menu hanging out in the upper left corner, so let's add a reposition() method to our Menu class just like we did with our MainDisplay class (in fact, you can simply copy and paste the method, if you so desire).
public function reposition(xPos:Number, yPos:Number):void {
this.x = Math.round(xPos);
this.y = Math.round(yPos);
}
Likewise, in our WebSite's onStageResize() method, we will need to call our Menu's reposition() method like so (we'll base our menu's y position on the y position of the MainDisplay and center its x position just like we did with the MainDisplay):
_menu.reposition(stage.stageWidth / 2 - _menu.width / 2, _mainDisplay.y + _mainDisplay.height + 15);
Now when you test your movie, you'll see that the menu is always 15 pixels below the bottom of our main display and centered horizontally. Now we're getting somewhere. Let's turn our attention to functionality, though. Sure it's all good and well to know that we clicked a button, but how can we figure out specifically which button was clicked? If we look back at our MenuButton class, we'll recall that we gave each button a private _idNum int property that could come in useful at this point. First, let's add a quick public getter method in order to retrieve that private _idNum property:
public function get id():int {
return _idNum;
}
That was easy. Now let's take a closer look at events in Actionscript 3. In Part I of this tutorial, it was hinted that the AS3 event objects contain properties that can prove themselves quite useful at times. Now, is one of those times. One of those event object properties is the "target" property. The target property of the event object contains a reference to the object instance that is listening for that particular event. Now, that we have a getter method to access our buttons' _idNum property, let's take a look at a concrete example of this target property in action. Back in our Menu's onClick() method, replace the current trace statement with this one:
trace ("you clicked button " + e.target.id);
Recall that "e" here is the MouseEvent object that is automatically passed to the onClick(method). Test your movie again and try clicking on a menu button. Now, that is much more useful - we now know exactly which button the user clicked and we can act accordingly (well, we won't right now, but at least we're on the right track to do so). The only trouble now is that we really don't want this event to be a part of our Menu class - we want this event to be accessed from our document class, WebSite.as. Luckily, the Sprite class, which our Menu class extends, itself, inherits from Flash's EventDispatcher class. What this gobbledy gook boils down to is that our Menu class can broadcast its own events. To do so, all we need to do is use the dispatchEvent() method (inherited from the EventDispatcher class) which accepts as an argument an event object. Well, as we've seen, we already have an event object ("e"). All we need to do is redispatch it. Easy peasy. So, for our Menu's onClick() method, let's 86 the trace statement and say this instead:
dispatchEvent(e);
Back in our WebSite class, let's now add an event listener to our _menu property. Seems most logical to do this in our addMenu() method, so let's do that. Note that since we're now listening for a MouseEvent, we'll also need to add import flash.events.MouseEvent to our ever growing import list.
private function addMenu():void {
_menu = new Menu();
_menu.addEventListener(MouseEvent.CLICK, onMenuClick);
addChild(_menu);
}
Of course we also need to add our onMenuClick() method. For now, let's just use it to trace the event object target property and see what object instance is calling that method.
private function onMenuClick(e:MouseEvent):void {
trace (e.target);
}
Test the movie, click a button, and bam. You'll see we immediately have yet another problem to overcome. Our onMenuClick() method is being called twice, first by our Menu, which we're really not interested in, and then by our MenuButton instance, which we want. Thankfully, the design of Actionscript 3's event structure has certain phases, the bubbling phase and the capturing phase. When adding an event listener, we can actually specify which of these two event phases to listen for. Ordinarily, the bubbling phase will work fine. In this case, though, we want to listen to our event in the capture phase. Don't worry if this doesn't make sense at the moment. Actionscript 3's event handling structure is both very sophisticated and very complicated. Suffice it to say that at this point, we want to set our addEventListener() method to listen for events in the capture phase and to do that, we pass a useCapture Boolean argument set to true like so:
_menu.addEventListener(MouseEvent.CLICK, onMenuClick, true);
Save your class and test the movie again. This time you'll see that the only object instance which calls our onMenuClick() method is the MenuButton which is exactly what we want. In fact, at this point you can now trace the e.target.id and once again, get the _idNum property of the button clicked. This is precisely the sort of behavior we're shooting for.
IS THIS THING ON?
Let's get back to something a little bit easier at this point. Let's add a little rollover noise to our menu buttons to see how sounds are handled with AS3. First, find an appropriate .mp3 file of a button click noise (or whatever), name it "click.mp3" and save it in the "sounds" directory we created in Part I. Once that's done, let's get back to our Menu class file.
In order to play sounds with Actionscript 3, we will need to add a SoundChannel as well as a Sound instance. These classes are located in the flash.media package which spells more imports for us. To load the sound file, we will also need to import the URLRequest class from the flash.net package. We can create a _buttonClick sound instance in our Menu constructor method, then in the init() method, we can use the URLRequest class to load our click.mp3 file into our _buttonClick sound property. Because this sound file is so small (mine is only 9k in size), we won't trouble ourselves with creating an onLoad event. We will, though, of course need a rollover event to trigger the sound. In Actionscript 3, the onRollOver event handler is now a constant of the MouseEvent class, called MOUSE_OVER. This event listener can be added to our button instances during the loop in our init() method, the same time the CLICK event is added. And, of course, once again we will need to add a method to play this sound. Going back to familiar AS2 phrases, we'll name this method "onRollOver". To get a sound to play in Actionscript 3, we must set our SoundChannel instance equal to our Sound.play() method. It sounds complicated, but it isn't really. If we named our SoundChannel instance "_sc", for example, and our Sound instance "_buttonClick", our onRollOver() method will simply look like this:
private function onRollOver(e:MouseEvent):void {
_sc = _buttonClick.play();
}
And there we have it. Our website is shaping up quite nicely. We now have a menu with three buttons that sits quite nicely beneath our main display. Each of the menu buttons click when you roll over them and each one will trace its own id number when you click on it. In the next segment, we will load some content text from a .xml file and show how these button id numbers can actually be put to good use to display our loaded content. We'll also, finally, get around to showing what all these color properties we've been adding to our classes can do. Once again, for posterity's sake, here are our complete classes at this point in time:
The document class - "classes.WebSite"
package classes {
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.BevelFilter;
import flash.filters.DropShadowFilter;
import classes.graphics.*;
import classes.iface.Menu;
public class WebSite extends Sprite {
private var _bg:Background;
private var _mainDisplay:MainDisplay;
private var _menu:Menu;
public function WebSite() {
init();
}
private function init():void {
initStage();
addBackground();
addMenu();
addMainDisplay();
onStageResize();
}
private function initStage():void {
stage.frameRate = 31;
stage.showDefaultContextMenu = false;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.addEventListener(Event.RESIZE, onStageResize);
}
private function onStageResize(... eventArray:Array):void {
_bg.resize(stage.stageWidth, stage.stageHeight);
_mainDisplay.reposition(stage.stageWidth / 2 - _mainDisplay.width / 2, stage.stageHeight / 2 - _mainDisplay.height / 2);
_menu.reposition(stage.stageWidth / 2 - _menu.width / 2, _mainDisplay.y + _mainDisplay.height + 15);
}
private function addBackground():void {
_bg = new Background();
addChild(_bg);
}
private function addMainDisplay():void {
_mainDisplay = new MainDisplay();
_mainDisplay.filters = [new BevelFilter(3, 90, 0xFFFFFF, .8, 0x000000, .65, 3, 3, 1, 3), new DropShadowFilter(8, 90, 0, .75, 7, 7, 1, 3)];
addChild(_mainDisplay);
}
private function addMenu():void {
_menu = new Menu();
_menu.addEventListener(MouseEvent.CLICK, onMenuClick, true);
addChild(_menu);
}
private function onMenuClick(e:MouseEvent):void {
trace (e.target.id);
}
}
}
Our two graphics classes, "classes.graphics.Background"
package classes.graphics {
import flash.display.Sprite;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.geom.Matrix;
public class Background extends Sprite {
private var _color:uint;
public function Background() {
_color = 0x669900;
drawBackground();
}
private function drawBackground():void {
var m:Matrix = new Matrix();
m.createGradientBox(800, 600);
graphics.beginGradientFill(GradientType.RADIAL, [0xE9FEB4, _color], [1,1], [0, 255], m, SpreadMethod.PAD);
graphics.drawRect(0, 0, 800, 600);
graphics.endFill();
}
public function resize(w:Number, h:Number):void {
this.width = w;
this.height = h;
}
}
}
and "classes.graphics.MainDisplay"
package classes.graphics {
import flash.display.Sprite;
import flash.display.Shape;
import flash.filters.DropShadowFilter;
import flash.text.TextField;
import flash.text.TextFormat;
public class MainDisplay extends Sprite {
private var _color:uint;
private var _tf:TextField;
public function MainDisplay() {
_color = 0x669900;
_tf = new TextField();
init();
}
private function init() {
drawDisplay();
addOverlay();
addTextField();
}
private function drawDisplay():void {
this.graphics.beginFill(_color);
this.graphics.drawRoundRect(0, 0, 450, 400, 20, 20);
this.graphics.endFill();
}
private function addOverlay():void {
var overlay = new Shape();
overlay.graphics.beginFill(0xFFFFFF, .4);
overlay.graphics.drawRoundRect(10, 10, 430, 380, 20, 20);
overlay.graphics.endFill();
overlay.filters = [new DropShadowFilter(2, 90, 0x000000, .8, 3, 3, 1, 3, true)];
addChild(overlay);
}
private function addTextField():void {
var fmt:TextFormat = new TextFormat("Tahoma", 12, 0x000000, null, null, null, null, null, null, null, null, null, 10);
_tf = new TextField();
_tf.multiline = true;
_tf.wordWrap = true;
_tf.width = 400;
_tf.height = 360;
_tf.x = 20;
_tf.y = 20;
_tf.defaultTextFormat = fmt;
addChild(_tf);
}
public function reposition(xPos:Number, yPos:Number):void {
this.x = Math.round(xPos);
this.y = Math.round(yPos);
}
}
}
and finally our user interface Menu class (combined with the helper MenuButton class) "classes.iface.Menu"
package classes.iface {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
public class Menu extends Sprite {
private var _btns:Array;
private var _btnLabels:Array;
private var _btnColors:Array;
private var _btnSpacing:Number;
private var _buttonClick:Sound;
private var _sc:SoundChannel;
public function Menu() {
_btns = new Array();
_btnSpacing = 120;
_btnLabels = new Array ("Home", "Product", "Contact");
_btnColors = new Array(0x669900, 0xCC0066, 0x6699FF);
_buttonClick = new Sound();
init();
}
private function init():void {
var numButtons:int = _btnLabels.length;
_buttonClick.load(new URLRequest("sounds/click.mp3"));
for (var i = 0; i < numButtons; i++) {
var b:MenuButton = new MenuButton(_btnColors[i], _btnLabels[i], i);
b.x = i * _btnSpacing;
addChild(b);
_btns.push(b);
b.addEventListener(MouseEvent.CLICK, onClick);
b.addEventListener(MouseEvent.MOUSE_OVER, onRollOver);
}
}
private function onClick(e:MouseEvent):void {
dispatchEvent(e);
}
private function onRollOver(e:MouseEvent):void {
_sc = _buttonClick.play();
}
public function reposition(xPos:Number, yPos:Number):void {
this.x = Math.round(xPos);
this.y = Math.round(yPos);
}
}
}
// imports for internal MenuButton class
import flash.display.Sprite;
import flash.display.SimpleButton;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.geom.Matrix;
import flash.filters.DropShadowFilter;
import flash.filters.BevelFilter;
import flash.filters.GlowFilter;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.text.AntiAliasType;
internal class MenuButton extends SimpleButton {
private var _upShadow:DropShadowFilter;
private var _downShadow:DropShadowFilter;
private var _overGlow:GlowFilter;
private var _bevel:BevelFilter;
private var _color:uint;
private var _idNum:int;
private var _btnText:String;
private var _newColor:uint;
public function MenuButton(nc:uint, btnText:String, id:int) {
_newColor = nc;
_btnText = btnText;
_idNum = id;
_color = 0x669900;
_bevel = new BevelFilter(4, 90, 0xFFFFFF, .8, 0x000000, .1, 0, 3, 1, 3);
_upShadow = new DropShadowFilter(3, 90, 0x000000, .6, 4, 4, 1, 3);
_downShadow = new DropShadowFilter(1, 90, 0x000000, .8, 2, 2, 1, 3);
_overGlow = new GlowFilter(0xFFFF66, .5, 10, 10, 1, 3, true);
_fmt = new TextFormat("Arial", 11, 0x000000, null, null, null, null, null, TextFormatAlign.CENTER);
drawButton();
}
public function drawButton():void {
this.upState = drawUp();
this.overState = drawOver();
this.downState = drawDown();
this.hitTestState = drawUp();
}
private function drawUp():Sprite{
var cols:Array = [_color, 0x222222];
var m:Matrix = new Matrix();
m.createGradientBox(100, 25, 90*Math.PI/180);
var s:Sprite = new Sprite();
s.graphics.beginGradientFill(GradientType.LINEAR, cols, [1, 1], [100, 255], m, SpreadMethod.PAD);
s.graphics.drawRoundRect(0, 0, 100, 20, 20, 20);
s.graphics.endFill();
var tf:TextField = buttonText();
s.addChild(tf);
s.filters = [_bevel, _upShadow];
return s;
}
private function drawDown():Sprite {
var cols:Array = [_color, 0x222222];
var m:Matrix = new Matrix();
m.createGradientBox(100, 25, 90*Math.PI/180);
var s:Sprite = new Sprite();
s.graphics.beginGradientFill(GradientType.LINEAR, cols, [1, 1], [100, 255], m, SpreadMethod.PAD);
s.graphics.drawRoundRect(0, 2, 100, 20, 20, 20);
s.graphics.endFill();
var tf:TextField = buttonText();
tf.y = 2;
s.addChild(tf);
s.filters = [_bevel, _downShadow];
return s;
}
private function drawOver():Sprite {
var cols:Array = [_color , 0x222222];
var m:Matrix = new Matrix();
m.createGradientBox(100, 25, 90*Math.PI/180);
var s:Sprite = new Sprite();
s.graphics.beginGradientFill(GradientType.LINEAR, cols, [1, 1], [100, 255], m, SpreadMethod.PAD);
s.graphics.drawRoundRect(0, 0, 100, 20, 20, 20);
s.graphics.endFill();
var tf:TextField = buttonText();
s.addChild(tf);
s.filters = [_overGlow, _bevel, _upShadow];
return s;
}
private function buttonText():TextField {
var fmt = new TextFormat("Tahoma", 10, 0x000000, null, null, null, null, null, TextFormatAlign.CENTER);
var tf:TextField = new TextField();
tf.width = 100;
tf.height = 20;
tf.antiAliasType = AntiAliasType.ADVANCED;
tf.selectable = false;
tf.defaultTextFormat = fmt;
tf.text = _btnText;
return tf;
}
public function get id():int {
return _idNum;
}
}
Hopefully this all made some sense to you and proved helpful. See ya in the next segment.