Categories Displayed in Flash

Archive for the 'events' Category

Flash: Working with Events: Get on the bus..Bubbling is your friend.

One of the great changes in AS3 is the DisplayList.   It when used appropriately, dramatically eases many things, one of my favorite is decoupling clip intercommunication, as the display list or any clip on it can act as a message bus.

Approach 1 : Display list heirarchy as message bus.

Say you have a monkey button shaped as a deep in a forest of tree graphics that you want to find out when it's been clicked on, even while it's jumping around from branch to branch (at different depths).   If a event setup on the click is set to bubble it doesn't matter where or how deep it is, it will eventually bubble up to the parent class so you can do something with it.

Similarly say you have a animated figure that you want to fire off when it's completed with it's animation, to do something else. While one possibility is the 'controller' reaching down to watch (e.g. using ENTER_FRAME, or addFrameScript(totalFrames-1..), or the clip reaching up and telling the main timeline to do something.  A much cleaner approach is listening for the event closer to the root (e.g. a document level class), and  dispatching an event on the last frame like:

dispatchEvent( new Event("FINISHED_ANIMATION", true, true)); //last help it bubble

 Approach 2: Stage as message bus.

Third is using a stage as a message bus, since every clip has access to it.   Especially when used with the ADDED_TO_STAGE event and dynamically loaded assets that have to perform some functions before being ready or passing a set of values to a caring class, when using an event that can carry a generic message, like EventWithArgs.

Flash: TextField actionscript hyperlink in AS3.0

In this article we cover how to get flash.text.TextFields HTML anchors/hyperlinks, to call actionscript functions, how to use CSS to style the up, over/hover and down/active and [UPDATE] visited states similar to hyperlinks in normal html.

The applications of this are numerous. Perhaps clicking a hyperlink will deeplink into your Flash application without navigating away, or perhaps it would open up a dictionary definition on the term.

(Either JavaScript is not active or you are using an old version of Adobe Flash Player. Please install the newest Flash Player.)
In this example clicking on the 3 links will show in the bottom text box where the link was meant to go.  This is based on the event handler receiving a flash.events.TextEvent.LINK event, as dispatched by formatting the '<a href=..' to dispatch the link event instead of the default navigateToURL.

The 'blog1' and 'blog2' links both go to the same URL, when either are clicked on both will change to the visited style.   This change color requires a hack, as the 'a:visited' isn't one of the CSS tags supported by Flash.   However we CAN overwrite the CSS style to display a different style, on the click.   This is more complex as we have to figure out, a) are we interested in changing all links that point to that location? or b) just the one that was clicked on?.   Keep in mind that this is a hack, and differs from the way browsers behave the links won't persist past that flash session, or across different TextFields unless you code it that way.

In a future article I'll discuss how to clean and process HTML, and replace the links with internal flash link's if you are using CMS'd, progressive enhancement websites, with Flash as the RIA style/presentation layer.  So the same HTML will work as expected when in HTML only site (e.g. for search bots) or in the Flash only.

  1.  
  2. import flash.text.StyleSheet;
  3. import flash.events.TextEvent;
  4.  
  5. var style:StyleSheet = new StyleSheet();
  6.  
  7. var hover:Object = new Object();
  8. hover.fontWeight = "bold";
  9. hover.color = "#00FF00";
  10. var link:Object = new Object();
  11. link.fontWeight = "bold";
  12. link.textDecoration= "underline";
  13. link.color = "#555555";
  14. var active:Object = new Object();
  15. active.fontWeight = "bold";
  16. active.color = "#FF0000";
  17.  
  18. var visited:Object = new Object();
  19. visited.fontWeight = "bold";
  20. visited.color = "#cc0099";
  21. visited.textDecoration= "underline";
  22.  
  23. style.setStyle("a:link", link);
  24. style.setStyle("a:hover", hover);
  25. style.setStyle("a:active", active);
  26. style.setStyle(".visited", visited); //note Flash doesn't support a:visited
  27.  
  28. html_txt.styleSheet = style;
  29. var htm:Array = new Array();
  30. htm.push("
  31. <p align="center">Example of HTML hyperlinks calling actionscript in AS3.0
  32.  
  33. ");
  34. htm.push("
  35.  
  36. Click to visit <a href="event:http://www.TroyWorks.com">home</a> ");
  37. htm.push("<a href="event:http://www.TroyWorks.com/blog/">blog1</a> ");
  38. htm.push("<a href="event:http://www.TroyWorks.com/blog/">blog2</a>");
  39. html_txt.htmlText= htm.join("");
  40.  
  41. function onHyperLinkEvent(evt:TextEvent):void {
  42.         trace("**click**"+ evt.text);
  43.         define_txt.text = ( "Clicked on " + evt.text);
  44.         var str:String = html_txt.htmlText;
  45.      //// update the link to the 'visited' state'
  46.  
  47.         str = str.split("'event:"+evt.text+"'").join("'event:"+evt.text+"' class='visited' ");
  48.         html_txt.htmlText = str;
  49. }
  50.  
  51. html_txt.addEventListener(TextEvent.LINK, onHyperLinkEvent);

Flash: 4 Tricks for using MovieClips as Buttons

So here are 4 tricks I've found useful:

  1. Use actionscript to create the Button behavior (enlarging etc), centralizing it in one place.
  2. Use clips styled in the IDE, and copy their style/filters for mouse over, down, disabled state.
  3. Use mouseEnabled (and alpha/brightness) to temporarily disable a button
  4. Use the instance name as the label, and in the event parsing.

(Either JavaScript is not active or you are using an old version of Adobe Flash Player. Please install the newest Flash Player.)

Styling MOUSE_OVER, MOUSE_DOWN States

Similar to CSS centralizing style, centralizing behavioral style offers the same maintenance advantages.   Using clips on the stage for style allows the designer to visually see what's desired, rather than spending countless iterations tweaking actionscript to get it right, it's a good handoff in team based approach.   I also sometimes use describeType 'autowire' any buttons of particular classes.   For the disabled state I stacked two brightness filters ontop of each other.

Styling it to send all the events to a central controller (MVC UCM style), allows things to be debugged far easier than when events are going everywhere, and the controller has internal state, respond appropriately, say ignoring all clicks during loading, without having to rewire all the various components.

For 1 and 2 here's the underlying code. Notice I use mouseChildren = false to keep the label textfield from generating events.

  1.  
  2. function configureMC_Button(ary:Array, addL:Boolean = true):void {var a:MovieClip;
  3.  
  4. var i:int = 0;
  5.  
  6. var n:int =ary.length;
  7.  
  8. for (true; i < n; ++i) {
  9.  
  10. a = ary[i];
  11.  
  12. if (addL) {
  13.  
  14. a.addEventListener(MouseEvent.MOUSE_OVER, onMouseOverHandler);
  15.  
  16. a.addEventListener(MouseEvent.MOUSE_OUT, onMouseOutHandler);
  17.  
  18. a.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
  19.  
  20. a.addEventListener(MouseEvent.MOUSE_UP, onMouseOverHandler);a.addEventListener(MouseEvent.CLICK, onMouseClickHandler);
  21.  
  22. a.mouseChildren =false;
  23.  
  24. } else {
  25.  
  26. a.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOverHandler);
  27.  
  28. a.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOutHandler);
  29.  
  30. a.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
  31.  
  32. a.removeEventListener(MouseEvent.MOUSE_UP, onMouseOverHandler);
  33.  
  34. a.removeEventListener(MouseEvent.CLICK, onMouseClickHandler);
  35.  
  36. }
  37.  
  38. }
  39.  
  40. }
  41.  
  42. function onMouseClickHandler(evt:Event):void {
  43.  
  44. var mc:MovieClip = MovieClip(evt.target);
  45.  
  46. output_txt.text =(" \r"+ mc.label_txt.text + "**Clicked **");
  47.  
  48. }
  49.  
  50. function onMouseDownHandler(evt:Event):void {
  51.  
  52. var mc:MovieClip = MovieClip(evt.target);
  53.  
  54. mc.filters = downTreatment.filters;
  55.  
  56. }
  57.  
  58. function onMouseOverHandler(evt:Event):void {
  59.  
  60. var mc:MovieClip = MovieClip(evt.target);
  61.  
  62. mc.scaleX = mc.scaleY = 1.2;
  63.  
  64. mc.filters = overTreatment.filters;
  65.  
  66. }
  67.  
  68. function onMouseOutHandler(evt:Event):void {
  69.  
  70. var mc:MovieClip = MovieClip(evt.target);
  71.  
  72. mc.scaleX = mc.scaleY = 1;
  73.  
  74. mc.filters = [];
  75.  
  76. }
  77.  
  78. overTreatment.visible = downTreatment.visible = disabledTreatment.visible = false;
  79.  
  80. configureMC_Button([a_btn, b_btn, c_btn, d_btn, e_btn, f_btn, g_btn]);

Enabling and Disabling via ActionScript

You can disable/enable a clip simply by using the mouseEnabled field which will turn on or off mouse events.   Contrast this with the approach of adding/removing listeners to enable or disable a button, which may have multiple listeners for different classes, you can't see, so you never know if you got them all.

  1.  
  2. function setEnabled(  ary:Array, enable:Boolean = true):void {var a:MovieClip;
  3.  
  4. var i:int = 0;
  5.  
  6. var n:int =ary.length;
  7.  
  8. for (true; i < n; ++i) {
  9.  
  10. a = ary[i];
  11.  
  12. if (enable) {
  13.  
  14. a.mouseEnabled = false;
  15.  
  16. a.filters =[];
  17.  
  18. } else {
  19.  
  20. a.mouseEnabled = false;
  21.  
  22. a.filters = disabledTreatment.filters;
  23.  
  24. }
  25.  
  26. }
  27.  
  28. }
  29.  
  30. setEnabled([d_btn, e_btn], false);

Using the instance name as id.

Using the instance as the basis for identification, saying parsing it for use the label, and in the event, can make things more uniform than having buttons dispatching events,.   In internationalized apps where the label changes based on whatever language is present, the instance name can serve as a key to finding the appropriate text in some multilanguage dictionary, or even show when a label is missing.

In Closing
Using a single movieClip has many advantages,  only one skin has to be created instead of 4-5 for a SimpleButton.   When handled in the way outlined above it has many of the advantages of being a component, without the work of being a full blown one.   It minimizes the number of classes/library items when each button is created with a unique label hard coded into it.

In conjunction with a tweening engine, it allows for what I call momentum styled buttons.   Buttons that don't have crisp state changes like SimpleButton attempts on the mouse up/over/down. Things that fade up, and down, like real world lightbulbs, cars.   When used with ColorTransform this can allow for adaptively styled UI, like that of CSS.

There are also safety benefits. In AS3.0 the timeline is purely script driven, there are still bugs when using multiple frames to manage state on particular versions of the flash player.   I've had odd issues with sound not firing or not stopping firing, components blowing up when in tweens or in timelines.

AS3: recreating onLoad, MovieClip.addFrameScript part duex

Recreating onLoad with MovieClip.addFrameScript

AS2 had an onLoad event, called after the frame's children had been created, which is largely how custom components were able to manipulate the visual state for useful things like:

  • register listeners.
  • hiding and showing clips (especially when multiple clips are stacked on top of each other for different error messages)
  • jumping to a frame,
  • setting up modal dialogs/mouse opaque areas

In AS3 this doesn't work the same, and this bites you on the occassion you have an actionscript class is registered to a class via the linkageID, like that of the "Document class.
If you try it you get the warning:

Warning: 1090: Migration issue: The onLoad event handler is not triggered automatically by Flash Player at run time in ActionScript 3.0. You must first register this handler for the event using addEventListener ( 'load', callback_handler).

So you think, ok, easy enough. Just add an eventListener to load. Test movie and then nothing happens. Search the help docs, and you find zero references to 'load' event. Check DisplayObject and you find every other event (render, added, etc) than the one we are looking for. Leads to one of two possibilities. One the load event doesn't exist... or second the load event has already been called prior to the class constructor being called, so adding a reference to it won't actually do anything. I can't really tell, I've tried searching and running some tests, and can't get it to work... which leads me to the question, just how do I accomplish all those in items above?

Thankfully the fix is easy: addFrameScript in the constructor.

addFrameScript(0,onLoadHandler); //works!!!
// addEventListener("load", onLoadHandler); // doesn't work

While on the subject,  in order to access clips on the  timeline from the *.as they should be public variables like this:

  • public var my_btn:SimpleButton
  • public var my_playerControls:MovieClip

and your class should be dynamic e.g

dynamic public class MyView extends MovieClip

Keeping the scripts already on the timeline.

Since your replacing the script call when it hits frame0, whatever is on the timeline won't be called. Thankfully since MovieClip is a dynamic class, scripts on the timeline are appended to the prototype, thus available by this which refers to the prototype chain instead of the class inheritance (Flash is multiple inheritance!). This is useful when the timeline has some configuration data/setup on it, and this approach allows you to call it prior or after your onLoadHandler

public function MyMovieClipClass(){

super();
addFrameScript(0,onLoadHandler);
// addEventListener("load", onLoadHandler); //doesn't work
trace(" AAAA CONSTRUCTOR AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
}
function onLoadHandler():void{
trace("BBBBB onLoadHandler BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
trace("onLoadHandler " + currentFrame);

///pre frameactions

if(!this["frame1"]){
this["frame1"]();
}

//post frameactions

my_btn.addEventListener(.....

my_playerControls.gotoAndStop("buffering");

}

OUTPUTS

  • AAAA CONSTRUCTOR
  • BBBB onLoadHandler
  • CCCC frame1TimelineScripts