4. Expander Class Continued: expandMe and contractMe functions

Now let's write the expandMe function:

private function expandMe(event:MouseEvent):void {
    _clip.removeEventListener(MouseEvent.CLICK, expandMe);
    _clip.gotoAndStop(2);
    twWidth = new Tween(_clip, "width", _easing, _startWidth, _endWidth, _duration, true);
    twHeight = new Tween(_clip, "height", _easing, _startHeight, _endHeight, _duration, true);
    twAlpha = new Tween(_clip, "alpha", _easing, _startAlpha, _endAlpha, _duration, true);
    twAlpha.addEventListener(TweenEvent.MOTION_FINISH, finishHandler);
    function finishHandler(event:TweenEvent) {
        twAlpha.removeEventListener(TweenEvent.MOTION_FINISH, finishHandler);
        _clip.addEventListener(MouseEvent.CLICK, contractMe);
    }
}

The first thing expandMe does is removes the event listener that called it. In most cases, when you remove an event listener, you can just copy and paste the same line where you added it, then just change the word "add" to "remove." We remove it because we don't want to allow a second click until after our animation completes. And the next click we get will be to collapse the box instead. The next thing this function does is switches frames on the _clip, so that we're animating the box that's on the second frame instead.

The Tween class takes seven parameters:

  1. The name of the object to Tween
  2. The property of the object to Tween (must be in quotes, as it's a String)
  3. The easing method to use
  4. The starting value
  5. The ending value
  6. The duration (can be expressed either in frames or seconds)
  7. Whether or not to use seconds to express duration (true or false)

As you can see in the function, four of these values are represented by variables, which, like I said before, makes it much easier to change them from one spot, but will become absolutely necessary later, when we write the setter functions. If you've worked with Tweens before, you know that as soon as you say new Tween(), and give it all those parameters, it goes! Unless you supply code to stop it, it just starts going immediately.

Those three tweens will run simultaneously, then. One tween animates the width, one animates the height, and the last one animates the alpha. They all use the same easing method, and they all have the same duration. We add an event listener to let us know when the animation is done, so that we can do some other stuff. This uses the TweenEvent class, and the event we want to monitor is the "MOTION_FINISH" event. There are three tweens going, but it's only necessary to add this event to one of them, and I just chose the last one... well, just because, that's all! Purely arbitrary. Notice that the handler function for the motion finish event is nested right here inside the outer function (expandMe). Nothing wrong with doing it this way. The thing to note here is that it's not necessary, nor is it allowed, to use an access modifier like "private" in front of it, because this function doesn't belong to the class when we do it this way.

When the motion is done, the _clip is fully expanded, and we remove the event listener for motion finish, then add an event listener to the _clip to listen for another mouse click. This one tells it to run the function "contractMe" when it's clicked (refer back to the last line in the above code box, not the following one).

Now, let's write "contractMe," which is going to look quite a bit like "expandMe" :

function contractMe(event:MouseEvent):void {
    _clip.removeEventListener(MouseEvent.CLICK, contractMe);
    twWidth = new Tween(_clip, "width", _easing, _endWidth, _startWidth, _duration, true);
    twHeight = new Tween(_clip, "height", _easing, _endHeight, _startHeight, _duration, true);
    twAlpha = new Tween(_clip, "alpha", _easing, _endAlpha, _startAlpha, _duration, true);
    twAlpha.addEventListener(TweenEvent.MOTION_FINISH, finishHandler);
    function finishHandler(event:TweenEvent) {
        twAlpha.removeEventListener(TweenEvent.MOTION_FINISH, finishHandler);
        _clip.gotoAndStop(1);
        initClip();
        _clip.addEventListener(MouseEvent.CLICK, expandMe);
    }
}

This function first removes the listener that called it. Then, the biggest difference here is that the starting and ending values given to the tweens are reversed in the parameter list, so that they all go from the larger value to the smaller. Also, when the motion finishes, the clip will be fully contracted, and we want the _clip to revert back to frame 1 again. I also found (when testing this) that it's necessary to call initClip again, to set the width and height of the _clip back to what it was when it started. Otherwise, the graphic on the first frame winds up extremely shrunken. Finally, the listener is added that will expand the _clip again.

So, you can see that expandMe and contractMe take turns being the handler for the _clip. Since the listener isn't reset until the motion completes, it's not possible to register a click on the _clip while it's in motion. I did that on purpose! However, should you decide that your help box should register clicks immediately, even if it's in mid-animation, that's easy to change, and I'm sure you won't have any problem figuring out how.

So now our class is complete except for the setter functions we'll write (I know, I keep talking about those!). At this point, it should be fully functional:

package com.mysite.effects {
    import flash.display.MovieClip;
    import flash.events.MouseEvent;
    import fl.transitions.Tween;
    import fl.transitions.TweenEvent;
    import fl.transitions.easing.*;
 
    public class Expander {
        private var _clip:MovieClip;
        private var _startWidth:Number;
        private var _endWidth:Number;
        private var _startHeight:Number;
        private var _endHeight:Number;
        private var _startAlpha:Number = 0.2;
        private var _endAlpha:Number = 1;
        private var _easing:Function = Bounce.easeOut;
        private var _duration:Number = 2;
        private var twWidth:Tween;
        private var twHeight:Tween;
        private var twAlpha:Tween;
 
        public function Expander(clip:MovieClip) {
            _clip = clip;
            _startWidth = _clip.width;
            _startHeight = _clip.height;
            _clip.gotoAndStop(2);
            _endWidth = _clip.width;
            _endHeight = _clip.height;
            _clip.gotoAndStop(1);
            _clip.buttonMode = true;
            _clip.addEventListener(MouseEvent.CLICK, expandMe);
            initClip();
        }
        private function initClip() {
            _clip.width = _startWidth;
            _clip.height = _startHeight;
            _clip.alpha = _startAlpha;
        }
        private function expandMe(event:MouseEvent):void {
            _clip.removeEventListener(MouseEvent.CLICK, expandMe);
            _clip.gotoAndStop(2);
            twWidth = new Tween(_clip, "width", _easing, _startWidth, _endWidth, _duration, true);
            twHeight = new Tween(_clip, "height", _easing, _startHeight, _endHeight, _duration, true);
            twAlpha = new Tween(_clip, "alpha", _easing, _startAlpha, _endAlpha, _duration, true);
            twAlpha.addEventListener(TweenEvent.MOTION_FINISH, finishHandler);
            function finishHandler(event:TweenEvent) {
                twAlpha.removeEventListener(TweenEvent.MOTION_FINISH, finishHandler);
                _clip.addEventListener(MouseEvent.CLICK, contractMe);
            }
        }
        private function contractMe(event:MouseEvent):void {
            _clip.removeEventListener(MouseEvent.CLICK, contractMe);
            twWidth = new Tween(_clip, "width", _easing, _endWidth, _startWidth, _duration, true);
            twHeight = new Tween(_clip, "height", _easing, _endHeight, _startHeight, _duration, true);
            twAlpha = new Tween(_clip, "alpha", _easing, _endAlpha, _startAlpha, _duration, true);
            twAlpha.addEventListener(TweenEvent.MOTION_FINISH, finishHandler);
            function finishHandler(event:TweenEvent) {
                twAlpha.removeEventListener(TweenEvent.MOTION_FINISH, finishHandler);
                _clip.gotoAndStop(1);
                initClip();
                _clip.addEventListener(MouseEvent.CLICK, expandMe);
            }
        }
    }
}

Now go back to the fla file again, and enter the following on frame 1 of the main timeline:

import com.mysite.effects.Expander;
var expander:Expander = new Expander(help);

Press Ctrl-Enter to test the movie. It should be running error free, and cause our "help" clip to expand and contract like a champ. The only thing left to do is to add some customization, which we'll do next. I'll also show you an easier way (than last tutorial) to make our _clip come to the front of everything else.