PDA

View Full Version : Creating buttons in AS3


Ricky55
08-08-2008, 07:08 PM
I've done quite a few projects in Flash but I tend to use it solidly for about three weeks and then I won't use it again until another project needs it so although I understand Action Script I never quite remember how to write it so I rely heavily on a bunch of examples that I've gathered over the years but these are all written in Action Script 2.

I've put off using AS3 for this very reason but the longer I leave it this worse this is going to get so I'm determined this time to use AS3 even though its taking me ages.

Anyway my problem, this is just an example but once I have the solution I will be able to use the code on my project. I have an FLA setup with some animated movie clips as buttons, now with some help I had them working in AS2 so that they would stay down until the next one was clicked just like a typical nav bar.

I have tried to write to translate the code into AS3 but I can only get some far, would one of you guys be kind enough to help me out with this. This is my code

[code]
var activeMenu = b1_mc;

b1_mc.addEventListener(MouseEvent.MOUSE_OVER, over);
b1_mc.addEventListener(MouseEvent.MOUSE_OUT, out);
b1_mc.addEventListener(MouseEvent.CLICK, clicked)

b2_mc.addEventListener(MouseEvent.MOUSE_OVER, over);
b2_mc.addEventListener(MouseEvent.MOUSE_OUT, out);
b2_mc.addEventListener(MouseEvent.CLICK, clicked)

b3_mc.addEventListener(MouseEvent.MOUSE_OVER, over);
b3_mc.addEventListener(MouseEvent.MOUSE_OUT, out);
b3_mc.addEventListener(MouseEvent.CLICK, clicked)

b4_mc.addEventListener(MouseEvent.MOUSE_OVER, over);
b4_mc.addEventListener(MouseEvent.MOUSE_OUT, out);
b4_mc.addEventListener(MouseEvent.CLICK, clicked)

function over(event:MouseEvent):void
{
if(_root.activeMenu == undefined || this._name != _root.activeMenu._name) {
this.gotoAndPlay(2);
}
}

function out(event:MouseEvent):void
{
if(_root.activeMenu == undefined || this._name != _root.activeMenu._name) {
this.gotoAndPlay(7);
}
}

function clicked(event:MouseEvent):void
{
if(_root.activeMenu != undefined) {
_root.activeMenu.gotoAndPlay(7);
}
// make menu active
this.gotoAndStop(20);
// save new menu item
_root.activeMenu = this;
}

I have even noticed that this.gotoAndPlay(2) doesn't even seem to work, do I have to write this differently in AS3?

wvxvw
08-08-2008, 10:50 PM
Yes, _root has gone =) (it was a bad practice to reference _root in AS2 too, but it happened too often =)
So, if you want to find out which clip has generated an event, you have to reffer event.target, this inside the listener function points to the object which declared it.
I assume that bN_mc's are some clips/buttons on the stage, and activeMenu is a variable that has to point to the currently selected one, right? If so, you may alter it to be something like this:
var aClip:MovieClip = this.getChildByName("a") as MovieClip;
var bClip:MovieClip = this.getChildByName("b") as MovieClip;
var hash:Dictionary = new Dictionary();

aClip.addEventListener(MouseEvent.CLICK, handleClick);
bClip.addEventListener(MouseEvent.CLICK, handleClick);
aClip.addEventListener(MouseEvent.MOUSE_OVER, handleOver);
bClip.addEventListener(MouseEvent.MOUSE_OVER, handleOver);

function handleClick(evt:MouseEvent):void
{
if(hash[evt.target] == "selected")
{
trace("clicking on the same clip");
} else {
hash = new Dictionary();
hash[evt.target] = "selected";
trace(hash[evt.target]);
}
}
function handleOver(evt:MouseEvent):void
{
if(hash[evt.target] == "hovered")
{
trace("hovering over the same clip");
} else {
hash = new Dictionary();
hash[evt.target] = "hovered";
trace(hash[evt.target]);
}
}

Ricky55
08-09-2008, 01:37 PM
Hi thanks for your reply, I don't really fully understand your code. I did try it but I don't understand it enough to be able to change it much. I have been reading a few other tips and I've managed to get the buttons working now but would you mind just helping me to get this menu working as per my action script 2 example. I'm desperate I need this done by Monday.

My example can be seen here
http://www.qwerty-design.co.uk/example2.html

My code now

b1_mc.addEventListener(MouseEvent.ROLL_OVER, onButtonOver);
b1_mc.addEventListener(MouseEvent.MOUSE_OUT, onButtonOut);
b1_mc.addEventListener(MouseEvent.CLICK, onButtonClicked);

b1_mc.buttonMode = true;
b1_mc.mouseChildren = false;

b2_mc.addEventListener(MouseEvent.ROLL_OVER, onButtonOver);
b2_mc.addEventListener(MouseEvent.MOUSE_OUT, onButtonOut);
b2_mc.addEventListener(MouseEvent.CLICK, onButtonClicked)

b2_mc.buttonMode = true;
b2_mc.mouseChildren = false;

b3_mc.addEventListener(MouseEvent.ROLL_OVER, onButtonOver);
b3_mc.addEventListener(MouseEvent.MOUSE_OUT, onButtonOut);
b3_mc.addEventListener(MouseEvent.CLICK, onButtonClicked);

b3_mc.buttonMode = true;
b3_mc.mouseChildren = false;

b4_mc.addEventListener(MouseEvent.ROLL_OVER, onButtonOver);
b4_mc.addEventListener(MouseEvent.MOUSE_OUT, onButtonOut);
b4_mc.addEventListener(MouseEvent.CLICK, onButtonClicked);

b4_mc.buttonMode = true;
b4_mc.mouseChildren = false;


function onButtonOver(event:MouseEvent):void
{
var btn:MovieClip = event.target as MovieClip;
btn.gotoAndPlay(2);
}

function onButtonOut(event:MouseEvent):void
{
var btn:MovieClip = event.target as MovieClip;
btn.gotoAndPlay(7);
}

function onButtonClicked(event:MouseEvent):void
{

var btn:MovieClip = event.target as MovieClip;
btn.gotoAndStop(20);

}

Ricky55
08-09-2008, 03:46 PM
Can any body please help me with this?

wvxvw
08-09-2008, 06:05 PM
package org.gfx.framework.view
{
import flash.display.Sprite;
import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.Elastic;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.geom.ColorTransform;
import org.gfx.framework.events.AccordeonEvent;
import org.gfx.framework.view.AniAccordeon;
import org.gfx.framework.utils.MathUtils;

/**
* AniButton class.
* @author wvxvw
*/
public class AniButton extends Sprite
{
private var _tween:Tween;
private var _background:Sprite;
private var _lable:TextField;
private var _overFormat:TextFormat;
private var _outFormat:TextFormat;
private var _selectedFormat:TextFormat;
private var _aniTransform:ColorTransform;

private var _lableText:String = "Button";
private var _width:int = 100;
private var _height:int = 100;
private var _backgroundColor:int = 0x444444;
private var _textShift:Number = 50;
private var _isSelected:Boolean = false;

public var aniprop:Number = 0;

public function AniButton()
{
super();
_overFormat = new TextFormat("_sans", 11, 0xFFFFFF);
_outFormat = new TextFormat("_sans", 11, 0xDDDDDD);
_selectedFormat = new TextFormat("_sans", 11, 0xFFDDAA);
_overFormat.indent = 5;
_outFormat.indent = 5;
_selectedFormat.indent = 5;
_aniTransform = new ColorTransform();
_aniTransform.color = _backgroundColor;
buttonMode = true;
mouseChildren = false;
_background = new Sprite();
addChild(_background);
draw();
_tween = new Tween(this, "aniprop", Elastic.easeOut, 0, 100, 2, true);
_tween.looping = false;
_tween.stop();
_tween.addEventListener(TweenEvent.MOTION_CHANGE, animationPlay);
_tween.addEventListener(TweenEvent.MOTION_START, animationStart);
_tween.addEventListener(TweenEvent.MOTION_FINISH, animationFinish);
}

public function onCollapse(evt:AccordeonEvent):void
{
if ((parent as AniAccordeon).selectedButton === this) return;
if ((parent as AniAccordeon).overButton === this && !_tween.isPlaying)
{
play();
} else {
stop();
rewind();
}
}

public function onSelect(evt:AccordeonEvent):void
{
if ((parent as AniAccordeon).selectedButton === this)
{
_isSelected = true;
stop();
fforward();
} else if (_isSelected)
{
_isSelected = false;
stop();
rewind();
}
}

public function play():void
{
_lable.setTextFormat(_overFormat);
_tween.start();
}

public function stop():void
{
_tween.stop();
}

public function rewind():void
{
_lable.setTextFormat(_outFormat);
_tween.rewind();
}

public function fforward():void
{
_lable.setTextFormat(_selectedFormat);
_tween.fforward();
}

private function animationStart(evt:TweenEvent):void
{

}

private function animationFinish(evt:TweenEvent):void
{
_lable.setTextFormat(_selectedFormat);
}

private function animationPlay(evt:TweenEvent):void
{
_lable.x = _textShift * aniprop / 100;
_lable.width = _width - _lable.x;
_aniTransform.color = MathUtils.colorPercent(_backgroundColor, aniprop);
_background.transform.colorTransform = _aniTransform;
}

public function draw():void
{
_background.graphics.clear();
_background.graphics.beginFill(_backgroundColor);
_background.graphics.drawRect(0, 0, _width, _height);
_background.graphics.endFill();
if (_lable)
{
if (contains(_lable)) removeChild(_lable);
}
_lable = new TextField();
_lable.selectable = false;
_lable.width = _width;
_lable.height = _height;
_lable.text = _lableText;
_lable.setTextFormat(_outFormat);
addChild(_lable);
}

public function set lable(value:String):void
{
_lableText = value;
draw();
}

public function get lable():String
{
return _lableText;
}

public override function get width():Number
{
return _width;
}

public override function set width(value:Number):void
{
_width = value;
draw();
}

public override function get height():Number
{
return _height;
}

public override function set height(value:Number):void
{
_height = value;
draw();
}

public function get isSelected():Boolean
{
return _isSelected;
}

public override function toString():String
{
return "[AniButton " + _lableText + "]";
}
}

}
package org.gfx.framework.view
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import org.gfx.framework.view.AniButton;
import org.gfx.framework.events.AccordeonEvent;

/**
* AniAccordeon class.
* @author wvxvw
*/
public class AniAccordeon extends Sprite
{
private var _buttons:Array;
private var _selectedButton:AniButton;
private var _overButton:AniButton;

public function AniAccordeon()
{
super();
}

public function addButton(lable:String):void
{
if (!_buttons) _buttons = [];
var button:AniButton = new AniButton();
button.lable = lable;
button.width = 300;
button.height = 20;
button.y = _buttons.length * 20;
button.addEventListener(MouseEvent.CLICK, handleButtonClick);
button.addEventListener(MouseEvent.MOUSE_OVER, handleButtonOver);
button.addEventListener(MouseEvent.MOUSE_OUT, handleButtonOut);
addEventListener(AccordeonEvent.COLLAPSE_ALL, button.onCollapse);
addEventListener(AccordeonEvent.SELECT, button.onSelect);
addChild(button);
_buttons.push(button);
}

private function handleButtonClick(evt:MouseEvent):void
{
_selectedButton = evt.target as AniButton;
dispatchEvent(new AccordeonEvent(AccordeonEvent.SELECT));
}

private function handleButtonOver(evt:MouseEvent):void
{
_overButton = evt.target as AniButton;
dispatchEvent(new AccordeonEvent(AccordeonEvent.COLLAPSE_ALL));
}

private function handleButtonOut(evt:MouseEvent):void
{
_overButton = null;
dispatchEvent(new AccordeonEvent(AccordeonEvent.COLLAPSE_ALL));
}

public function get selectedButton():AniButton
{
return _selectedButton;
}

public function get overButton():AniButton
{
return _overButton;
}
}

}
package org.gfx.framework.events
{
import flash.events.Event;

/**
* AccordeonEvent class.
* @author wvxvw
*/
public class AccordeonEvent extends Event
{
public static const COLLAPSE_ALL:String = "collapseAll";
public static const SELECT:String = "select";

public function AccordeonEvent(type:String)
{
super(type);
}

public override function clone():Event
{
return new AccordeonEvent(type);
}

public override function toString():String
{
return formatToString("AccordeonEvent", "type");
}

}

}
package org.gfx.framework.utils
{

/**
* MathUtils class.
* @author wvxvw
*/
public class MathUtils
{

public static function colorPercent(color:int, percent:int = 100):int
{
if (percent > 100) percent = 100;
if (percent < 0) percent = 0;
var r:int = (color >> 16 & 0xFF) * percent / 100;
var g:int = (color >> 8 & 0xFF) * percent / 100;
var b:int = (color & 0xFF) * percent / 100;
return (r << 16) + (g << 8) + b;
}

}

}
package
{
import flash.display.Sprite;
import org.gfx.framework.view.AniAccordeon;
import org.gfx.framework.view.EventCatcher;

public class Main extends Sprite
{
public var aniAccordeon:AniAccordeon;

public function Main():void
{
aniAccordeon = new AniAccordeon();
aniAccordeon.addButton("Portfolio");
aniAccordeon.addButton("Photographs");
aniAccordeon.addButton("Resume");
aniAccordeon.addButton("Contact me");
addChild(aniAccordeon);
}
}
}
Monday saved? ;)

Ricky55
08-09-2008, 07:05 PM
Wow thats alot of code.

Could you just explain it a little for me?

What will all this do create the entire menu in action script?

Is there not an easy way to just modify my exiting code to make this menu work?

Thanks and sorry to be a pain.

Ricky55
08-09-2008, 07:23 PM
Just been given this code what does this look like a decent solution:

var lastClickedBtn:MovieClip;

function onButtonOver(event:MouseEvent):void
{
var btn:MovieClip = event.target as MovieClip;
btn.gotoAndPlay(2);
}

function onButtonOut(event:MouseEvent):void
{
var btn:MovieClip = event.target as MovieClip;
if(btn != lastClickedBtn){
btn.gotoAndPlay(7);
}
}

function onButtonClicked(event:MouseEvent):void
{
lastClickedBtn.gotoAndPlay(7);
var btn:MovieClip = event.target as MovieClip;
lastClickedBtn = btn;
btn.gotoAndStop(20);

}

sekasi
08-09-2008, 07:25 PM
Way to shrug off proper code solutions and have people do your work when you settle for the worst possible solution :(

kudos to Wwwvvwwvvyxxw for trying.

wvxvw
08-09-2008, 07:34 PM
This will actually significantly simplify your final code :)
You just copy-pase the code from my previous post, save it to the AS files:
<your project directory>/org/gfx/framevork/view/AniButton.as
<your project directory>/org/gfx/framevork/view/AniAccordeon.as
<your project directory>/org/gfx/framevork/events/AccordeonEvent.as
<your project directory>/org/gfx/framevork/utils/MathUtils.as
And the last part of the code is just an example for how you use it. I.e. if you will compile it from Flash IDE, than your code will look smth like this:

import org.gfx.framework.view.AniAccordeon;
import org.gfx.framework.view.AniButton;
import org.gfx.framework.events.AccordeonEvent;
var aniAccordeon:AniAccordeon = new AniAccordeon();
aniAccordeon.addButton("Portfolio");
aniAccordeon.addButton("Photographs");
aniAccordeon.addButton("Resume");
aniAccordeon.addButton("Contact me");
addChild(aniAccordeon);
aniAccordeon.addEventListener(AccordeonEvent.SELEC T, reactToSelectEvent);
var selectedButton:AniButton;
function reactToSelectEvent(evt:AccordeonEvent):void
{
selectedButton = (evt.target as AniAccordeon).selectedButton;
//put your code here that will respond to the selection event.
}
And, sorry, never mind EventCatcher class it's unrelated to this project, just the code gets messy if I try to edit it here...

What do you have in these classes:
AniButton - the class that creates an animated button, but you do not create buttons directly, you use AniAccordeon.addButton("buttonLable") instead.
This button has some predefined animation which it will play when hovered or pressed.

AniAccordeon - this class creates a menu, a set of buttons. When one of the buttons is pressed it dispatches AccordeonEvent.SELECT event, and it's property selectedButton points to the selected button.

AccordeonEvent - is an event that may be dispatched by AniAccordeon on two occasions: when the button is selected, and when the buttons need to rewind their animation to the initial state (you'd hardly need the second).

MathUtils - is a class for different math operations, (these all classes are simplified parts of another bigger framework, so, I didn't copy all the content). The button uses one function from it to calculate the background color. (again, you don't even have to know it exists =) )

stringy
08-09-2008, 08:05 PM
This will actually significantly simplify your final code :)
You just copy-pase the code from my previous post, save it to the AS files:
<your project directory>/org/gfx/framevork/view/AniButton.as
<your project directory>/org/gfx/framevork/view/AniAccordeon.as
<your project directory>/org/gfx/framevork/events/AniAccordeonEvent.as
<your project directory>/org/gfx/framevork/utils/MathUtils.as
And the last part of the code is just an example for how you use it. I.e. if you will compile it from Flash IDE, than your code will look smth like this:
import org.gfx.framework.view.AniAccordeon;
import org.gfx.framework.view.AniButton;
import org.gfx.framework.events.AniAccordeonEvent;
aniAccordeon = new AniAccordeon();
aniAccordeon.addButton("Portfolio");
aniAccordeon.addButton("Photographs");
aniAccordeon.addButton("Resume");
aniAccordeon.addButton("Contact me");
addChild(aniAccordeon);
aniAccordeon.addEventListener(AniAccordeonEvent.SE LECT, reactToSelectEvent);
var selectedButton:AniButton;
function reactToSelectEvent(evt:AniAccordeonEvent):void
{
selectedButton = (evt.target as aniAccordeon).selectedButton;
//put your code here that will respond to the selection event.
}
And, sorry, never mind EventCatcher class it's unrelated to this project, just the code gets messy if I try to edit it here...
What do you have in these classes:
AniButton - the class that creates an animated button, but you do not create buttons directly, you use AniAccordeon.addButton("buttonLable") instead.
This button has some predefined animation which it will play when hovered or pressed.
AniAccordeon - this class creates a menu, a set of buttons. When one of the buttons is pressed it dispatches AccordeonEvent.SELECT event, and it's property selectedButton points to the selected button.
AniAccordeonEvent - is an event that may be dispatched by AniAccordeon on two occasions: when the button is selected, and when the buttons need to rewind their animation to the initial state (you'd hardly need the second).
MathUtils - is a class for different math operations, (these all classes are simplified parts of another bigger framework, so, I didn't copy all the content). The button uses one function from it to calculate the background color. (again, you don't even have to know it exists =) )

Is there some confusion here with AniAccordeonEvent and AccordeonEvent?

wvxvw
08-09-2008, 08:08 PM
Uh... sorry, didn't notice it... I'll correct it. Should be AccordeonEvent, there's no AniAccordeonEvent.

Ricky55
08-09-2008, 10:11 PM
wvxvw, thanks for all your help mate but as a beginner I don't know where to start. I don't like to just copy and paste blindly without knowing ruffly what I'm pasting. For the time being I think I'm better off working on the time line and using simple actions to achieve what I want. I've never worked with external as files.

The code that I have now is not far from working, its not as clever as your code but at least I know how to work with it, all I need now is for one of the buttons to be down from the start and prevent an error from appearing when I click one of the buttons.

The error I get is
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at Test_fla::MainTimeline/onButtonClicked()

And my code now looks like this

b1_mc.addEventListener(MouseEvent.MOUSE_OVER, onButtonOver);
b1_mc.addEventListener(MouseEvent.MOUSE_OUT, onButtonOut);
b1_mc.addEventListener(MouseEvent.CLICK, onButtonClicked);

b1_mc.buttonMode = true;
b1_mc.mouseChildren = false;

b2_mc.addEventListener(MouseEvent.MOUSE_OVER, onButtonOver);
b2_mc.addEventListener(MouseEvent.MOUSE_OUT, onButtonOut);
b2_mc.addEventListener(MouseEvent.CLICK, onButtonClicked);

b2_mc.buttonMode = true;
b2_mc.mouseChildren = false;

b3_mc.addEventListener(MouseEvent.MOUSE_OVER, onButtonOver);
b3_mc.addEventListener(MouseEvent.MOUSE_OUT, onButtonOut);
b3_mc.addEventListener(MouseEvent.CLICK, onButtonClicked);

b3_mc.buttonMode = true;
b3_mc.mouseChildren = false;

b4_mc.addEventListener(MouseEvent.MOUSE_OVER, onButtonOver);
b4_mc.addEventListener(MouseEvent.MOUSE_OUT, onButtonOut);
b4_mc.addEventListener(MouseEvent.CLICK, onButtonClicked);

b4_mc.buttonMode = true;
b4_mc.mouseChildren = false;

var lastClickedBtn:MovieClip;

function onButtonOver(event:MouseEvent):void {
var btn:MovieClip = event.target as MovieClip;
btn.gotoAndPlay(2);
}

function onButtonOut(event:MouseEvent):void {
var btn:MovieClip = event.target as MovieClip;
if (btn != lastClickedBtn) {
btn.gotoAndPlay(7);
}
}

function onButtonClicked(event:MouseEvent):void {
lastClickedBtn.gotoAndPlay(7);
var btn:MovieClip = event.target as MovieClip;
lastClickedBtn = btn;
btn.gotoAndStop(20);
}


If you could give me an hand with this I would be very grateful.

sekasi
08-10-2008, 12:12 AM
Like I said, you should check out wwwvvwwvwvxxxwvs's code since it's proper AS3.

Was watching the olympics and wrote a timeline version for you though ; ) Should be easy enuf to understand.


var lastClickedBtn:int = -1;
var clips:Array = [];

for (var i:int = 1; i < 5; i++) {
this["b"+i+"_mc"].addEventListener(MouseEvent.MOUSE_OVER, onButtonOver);
this["b"+i+"_mc"].addEventListener(MouseEvent.MOUSE_OUT, onButtonOut);
this["b"+i+"_mc"].addEventListener(MouseEvent.CLICK, onButtonClicked);
this["b"+i+"_mc"].buttonMode = true;
this["b"+i+"_mc"].mouseChildren = false;
this["b"+i+"_mc"].id = i;
clips.push(this["b"+i+"_mc"]);
};

function onButtonOver(e:MouseEvent):void {
if (lastClickedBtn != e.target.id) e.target.gotoAndStop(10);
}

function onButtonOut(e:MouseEvent):void {
if (lastClickedBtn != e.target.id) e.target.gotoAndStop(1);
}

function onButtonClicked(e:MouseEvent):void {
if (lastClickedBtn != e.target.id) {
if (lastClickedBtn != -1) clips[(lastClickedBtn-1)].gotoAndStop(1);
e.target.gotoAndStop(20);
lastClickedBtn = e.target.id;
};
}

wvxvw
08-10-2008, 05:52 AM
Just uploaded the project files in case anyone may need them. =)

Ricky55
08-10-2008, 01:35 PM
Thanks so much, I do appreciate it.

I'm going to use the timeline method for now but I've saved the download and when I get a bit of time next week I'm gonna have a proper look at how you did that. I see what you mean now about how the code is reusable and how it would save time in the long term. I'll be able to use this on future jobs so thanks again.

stringy
11-02-2008, 06:50 PM
Great stuff from wvxvw.
Also of interest maybe http://www.kirupa.com/forum/showthread.php?t=272658