Now that I have finished the BaseComponent class I think it's time to build a first component. The secret to creating a scalable framework is to start by building the simplest components, then combine these to build the complex ones. I'm going to start with a simple animated button.

Flash has had a Button primitive since the very early days. Buttons are special MovieClips that contain only four frames. Each frame is designated to one of the states: Up, Over, Down and Hit. The Hit frame defines the active mouse area and the other three contain the graphical presentation states for the button. The main limitation of Button is that you only get one frame per state. Getting the individual states to animate can be done but it's a bit of hack - you needed to create three MovieClips and place them in each state of the button.

How the AnimatedButton class works
What I've tried to do with the AnimatedButton class is to keep all the animation inside a single MovieClip called 'states'. A standard Button called 'hotspot' defines the hit area for the AnimatedButton. The result is an easily editable and smoothly animated button.

🟥
This post previously contained Flash content.

AnimatedButton demo: bird animation for Psygnosis by John Dalziel 1990

If you download the source you'll see just how simple the code is. The class extends BaseComponent so all the memory management is siloed in the init() and destroy() functions - it's here that the hotspot event listeners are added and removed. Notice that the class does not track clicks. If you need to capture clicks you can listen for Mouse.CLICK events inside the parent movie (Content.as in this demo).

Much like a simple Button, the hit area is kept separate from the graphics. This stops the animation from dynamically changing the size and shape of the hit area. Skinning an AnimatedButton is as simple as editing the 'states' movie. Repositioning or resizing an AnimatedButton is as simple as scaling or positioning the 'hotspot' movie.

One final old-school Flash trick
This hotspot resizing can even be done in code thanks to an old-school Flash trick. Normally an 'empty' Flash button (that is a button that contains only a hit area) cannot be resized in code because it has no graphical assets and therefore it has no size! The button I'm using contains a MovieClip block with its alpha set to zero. This stops it being visible but because a graphic exists it can be re-sized from code.

The Code: AnimatedButton.as

package org.computus.ui
{
  import flash.events.*;
  import flash.display.SimpleButton
  import flash.display.MovieClip
  import org.computus.core.BaseComponent
  import org.computus.core.IComponent public

  class AnimatedButton extends BaseComponent implements IComponent
  {
    // ------------------------------------------
    // PROPERTIES
    public var hotspot:SimpleButton

    // hit area
    public var states:MovieClip

    // animated states 

    // ------------------------------------------
    // CONSTRUCTOR
    public function AnimatedButton():void {}

    // ------------------------------------------
    // MEMORY MANAGEMENT
    override public function init():void
    {
      states.mouseChildren = false
      hotspot.useHandCursor = true
      hotspot.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
      hotspot.addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
      hotspot.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
    }

    override public function destroy():void
    {
      states.stop()
      hotspot.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
      hotspot.removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
      hotspot.removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
    }

    // ------------------------------------------
    // EVENTS
    private function mouseDownHandler(event:MouseEvent):void
    {
      states.gotoAndPlay("down")
    }
    
    private function mouseOutHandler(event:MouseEvent):void
    {
      states.gotoAndPlay("out")
    }

    private function mouseOverHandler(event:MouseEvent):void
    {
      states.gotoAndPlay("over")
    }
  }
}