Adobe Flash is unique in that the content you create with it is intrinsically artistic and programmatic. Even if you are working by yourself, design and development must work in harmony in order for a piece to reach its full potential. With ActionScript 3, Flash has become a far more powerful tool—and in some ways, far more intimidating.

Exploring the Timeline

Let me start with a news flash for all you developers out there. The Timeline is not your enemy. The Timeline is your friend. The Timeline is your ally. Use the Timeline.

Wait, the Timeline?

Developers are taught that attaching artwork to the Stage dynamically is better because you can keep the Timeline organized. This concept is sound on paper. In practice, however, it's not as useful as you would think.

In this case, I'm going to put my preloader smack dab in the center of the Stage. Why? Because it saves me time and I don't have to calculate the center of the display area. I don't have to write x = width * stageWidth/2 + someOffset or whatever. Flash has done all the math for me, so that I (or another designer) can focus on positioning the preloader right where I want it.

My preloader is now centered on the Stage, and I've updated the Timeline for the preloader with frame labels (see Figure 1).

Oh dear, first the Timeline; now frame labels? Why would I ever do such a thing?!

Frame labels let me space out the animations. This saves me time because I don't need to keep notes that the animation first appears on Frame 2 and then begins to disappear on Frame 37. I know those activities occur at the frames labeled "IN" and "OUT."

By leveraging the Timeline for animation, artwork, and placement, I've freed myself of all the annoying (albeit simple) quirks of displaying the preloader on the Stage. Further, I've empowered designers to reskin the artwork easily. Do you want to add 10 frames to the "IN" animation? No problem. Do you need to change the preloader from green to purple? Sure, why not? Can you make the preloader look like a wine glass that fills up as the site loads? Can do.

In other words, the Timeline offers flexibility to modify the look and feel of a project without changing the fundamental purpose of the clip itself—to preload content.

Step-by-step overview of the preloader timeline

My preloader must be designed to accommodate many different variations if it's to be easily reused. Here's the order of operations:

  1. When I'm ready to start loading, I'll tell the preloader timeline to play "IN."
  2. Once the preloader is animated in, I'll assume that the movie clip named progress_mc is on the Stage.
  3. As the loading process progresses, I'll calculate the current percent loaded and play progress_mc to a corresponding frame.
  4. The moment progress_mc has reached the end of its timeline (which correlates to 100% loaded), I'll tell the preloader timeline to play "OUT."
  5. At the time that the preloader finishes its "OUT" animation, the site, app, whatever will be alerted that the preloader is finished. The preloader is then reset for reuse later in the project.

Pretty straightforward, right? Except at this point, you may be wondering how to tell when the "IN" and "OUT" animations have resolved. In the next part of this article, we'll take a look at how that works.

Events, listeners, event dispatching, and event hooks

ActionScript 3 is centered around the concept of event dispatching. If you have previous experience working with ActionScript 1.0 or 2.0, you might already know a little bit about this process without even realizing it. Have you ever written code like this?

var mouseListener = new Object(); mouseListener.onMouseDown = function() { trace("The mouse button is down."); }; Mouse.addListener(mouseListener);

In the code above, the mouseListener object is listening for an event (onMouseDown) from the Mouse class. When that event occurs—which we know happens when the user presses the mouse button down—Mouse will dispatch an event, which it calls onMouseDown. Anything listening for that event—in this case, mouseListener—will fire its corresponding onMouseDown().

The beauty of event dispatching is that multiple objects can listen for the same event and react differently as the event occurs. The following example illustrates how two listeners can respond to the same event:

var mouseListener1 = new Object(); var mouseListener2 = new Object(); mouseListener1.onMouseDown = function() { trace("the mouse button is down."); }; mouseListener2.onMouseDown = function() { trace("1+1=" + (1+1)); }; Mouse.addListener(mouseListener1); Mouse.addListener(mouseListener2);

Now Mouse has two listeners. Each is listening for the same event, but doing something different when the event occurs. In the example above, both listeners are tracing messages to the Output panel, but there's no reason mouseListener1 couldn't do one thing while mouseListener2 does something wildly different.

When you get into object-oriented programming, specifically building classes, this is really great because classes can handle the same events independent from one another without "knowing" what the other classes are doing.

Wait, I still don't get it

Let's back away from code for a second. Pretend for a second that your code is a fast-food restaurant (The ActionScript Café), the user (customer) is a patron, and s/he is hungry (mmm, that smells good).

The customer would like to order something. Now there are two ways your restaurant can go from here:

  • Scenario 1: The customer tells a waiter what he/she wants. The waiter or the user then tells the cook. Either the waiter, the customer, or the cook. And on and on. On top of not being particularly efficient, there can be lots of confusion. Who's telling whom what to do?
  • Scenario 2: The customer pushes a button on a touch screen (sorry waiter, you're out of a job). The cashier and cook are alerted. The cook knows how to cook the food, the cashier knows how to handle the cash register. The cook doesn't care about the money; the cook just assumes that the customer is going to pay for the food. The cashier doesn't care about the food; if there's a problem with the food, there's no way it's the cashier's fault.

In programmer-speak, Scenario 1 is the old-fashioned programming methodology you're used to. Bugs are difficult to squash because it's hard to tell keep track of what's happening at any point.

Scenario 2 is much more ideal. If there's a problem with the food, you need to look to fix the Cook class. If there's a problem with the cash flow, it's in the Cashier class. If there's a problem with both, maybe it with the TouchScreen class. Any way you slice it, the code is explicitly more organized and thus easier to manage.

Event handling in ActionScript 3

Let's talk about how events come into play in ActionScript 3.0—and, more specifically, in regards to building this preloader. In ActionScript 3 (and even ActionScript 2), you can define your own events. Furthermore, movie clips extend Event Dispatcher, meaning that movie clips can dispatch events in much the same way that the Mouse example above does.

Why is that strategy useful? I'll show you. Let's use the preloader as an example. If you take a look back at Figure 1, you'll notice there's code on the Timeline at the "IN_IDLE" frame label:

import com.bigspaceship.events.AnimationEvent; dispatchEvent(new AnimationEvent(AnimationEvent.ANIMATE_IN)); stop();

The import statement grabs an external class (I'll cover that part in a minute) for use in the Timeline. In this case, it's my custom event, AnimationEvent. I'm going to dispatch AnimationEvent.ANIMATE_IN. That event is a simple string. Similar to knowing that the mouse is down by virtue of the occurrence of an onMouseDown event in ActionScript 1.0, my listeners will wait for AnimationEvent.ANIMATE_IN and know that the Timeline has finished animating the section with the "IN" frame label.

The code is very similar for the "OUT_IDLE" frame label:

import com.bigspaceship.events.AnimationEvent; dispatchEvent(new AnimationEvent(AnimationEvent.ANIMATE_OUT)); stop();

By using dispatchEvent, I know when the Timeline has resolved the "OUT" animation. I call these event hooks. While "IN" and "OUT" are animating, my code simply waits and listens. Once I've reached an event hook, I can program the logic to proceed with the preloading process.

For more information about event handling, check out Trevor McCauley's article, Introduction to event handling in ActionScript 3.

Turning MovieClip into PreloaderClip

In the previous section of this article, I illustrated how you can employ multiple listeners to respond to a single event in different ways. I also provided an example of creating a custom event, and showed how movie clips can dispatch events to indicate the completion of activity within the Timeline. This strategy makes it possible to call functions at a specific time, which is exactly what is needed for your preloader to accurately display the percent loaded graphically to the user. In the code examples, I also touched on the concept of using an import statement to make an external class available to the project. Now you'll take a closer look at ActionScript classes and see why modular coding practices are useful.

Class structure

Classes are fundamentally just a different way of writing code. For the uninitiated, moving from simple coding to developing classes can be mortifying. Curiously, with experience, you'll find that ActionScript classes are far easier to read and understand than the simple, quick coding you're used to.

A simple class looks like this:

package { public class MyClass { var myVar:String // this is a class variable, otherwise known as a property. function MyClass() { } // this is called the constructor. When you create new MyClass(), this function automatically runs. function doSomething() {} // this is a class function, aka a method. I could call this to tell the class to quite literally do something. } }

Classes can utilize other classes by using import, which I've already utilized on the Timeline. In our example, event hooks imported com.bigspaceship.events.AnimationEvent.

There are many books that cover classes and object-oriented programming in Flash, but if I could point to one resource that helped me understand all of this, Colin Moock's Essential ActionScript 3 would be it. Colin's book is at my desk at all times.

Building the PreloaderClip engine

PreloaderClip is a very specific kind of movie clip. It has all of the regular movie clip methods (startDrag and stopDrag, for example) and properties (x, y, and alpha), but it also has its own custom functionality to handle loading.

In order to utilize the MovieClip class in this way, we'll need to extend it. Here's how to do that:

package com.bigspaceship.frameworks.site { import flash.display.MovieClip public class PreloaderClip extends MovieClip { function PreloaderClip() {}; }; };

The key syntax is in line 4 above: extends MovieClip. PreloaderClip extends the MovieClip class; it's a movie clip and then some. Makes sense, right?

PreloaderClip has stop(); on the first frame. That way the preloader artwork won't appear unless I command it to do so. I want it to play that section with the frame label "IN" in the Timeline, so I'll make a simple method to do that:

public function animateIn():void { gotoAndPlay("IN"); addEventListener(AnimationEvent.ANIMATE_IN, _onPreloaderIn,false,0,true); };

This method tells the timeline to play "IN" and then listen for my event. When the event happens, it automatically calls _onPreloaderIn because that's the method I specified to handle AnimationEvent.ANIMATE_IN. Here's what _onPreloaderIn looks like:

private function _onPreloaderIn($evt:Event):void { progress_mc.addEventListener(Event.COMPLETE,_onProgressBarComplete,false,0,true); progress_mc.addEventListener(Event.ENTER_FRAME,_onProgressEnterFrame,false,0,true); progress_mc.stop(); dispatchEvent(new Event(Event.INIT)); };

PreloaderClip assumes a movie clip is nested inside its Timeline named progress_mc. I tell progress_mc to stop (in case it decides to display the load progress before I'm ready) and listen to two new events: Event.COMPLETE and Event.ENTER_FRAME. Now ENTER_FRAME is the new way to handle onEnterFrame: every frame _onProgressEnterFrame will be called. (More on that in just a second.) Event.COMPLETE is an event hook at the end of the progress_mc timeline. When progress_mc gets to the end of that timeline, loading is finished.

That dispatchEvent line is an important one. Here the PreloaderClip is telling any listeners that something happened. Just like my event hooks, I can addEventListener() somewhere else to know when the preloader is ready and to ensure that I can begin loading.

Let's look at the method I've mapped ENTER_FRAME to, namely _onProgressEnterFrame:

private function _onProgressEnterFrame($evt:Event):void { (_targetFrame > progress_mc.currentFrame) ? progress_mc.play() : progress_mc.stop(); var totalPct:Number = Math.round((progress_mc.currentFrame/progress_mc.totalFrames) * 100); try { pct_mc.tf.text = totalPct.toString(); } catch($error:Error) { Out.debug(this,"% loaded: " + totalPct.toString()); } };

On every frame I'll compare _targetFrame against progress_mc's currentFrame. If my target frame is greater than the current frame, then play; otherwise stop. In addition, I wrote a simple way to display the percent loaded in a text field to the user. If the text field exists (in this case it should be nested in a movie clip named pct_mc and given an instance name of tf), then it will write the total percent in the text field. Otherwise I'll just trace the information to the Output panel.

The next part involves figuring out targetFrame. Here's how this is accomplished:

public function updateProgress($bytesLoaded:Number, $bytesTotal:Number, $itemsLoaded:Number, $itemsTotal:Number):void { var framesPerItem:Number = Math.floor(progress_mc.totalFrames/($itemsTotal-1)); var pct:Number = $bytesLoaded/$bytesTotal; _targetFrame = Math.floor(framesPerItem * pct) + (framesPerItem * $itemsLoaded); };

Ahhhhh, the secret is revealed. PreloaderClip actually doesn't load anything. It just displays the progress! I'll load the files I need from somewhere else—somewhere external. As data loads in, I'll tell PreloaderClip the following:

  • The bytesLoaded of the current loading item
  • The bytesTotal of that same item
  • The current item I'm loading
  • The total number of items loaded

In other words, I'll load each item sequentially. If I have an XML file, a SWF, and a JPEG to load, I'll first load the XML. The current item I'm loading is 0 of 2 (remember, always count from 0!). Then when the XML load is finished, I'll load a SWF. That item is counted as 1 of 2. Finally, I'll load the JPEG file. The current item I'm loading is 2 of 2. When the JPEG finishes loading, progress_mc will be at the end of its timeline. The user will know that the content is 100% loaded and PreloaderClip will receive the Event.COMPLETE event, which will automatically fire _onProgressBarComplete():

private function _onProgressBarComplete($evt:Event = null):void { _isLoadComplete = true; progress_mc.removeEventListener(Event.ENTER_FRAME,_onProgressEnterFrame); _animateOut(); }; private function _animateOut():void { gotoAndPlay("OUT"); addEventListener(AnimationEvent.ANIMATE_OUT, _onPreloaderOut,false,0,true); };

In the function above, preloading is complete, so we play the section of the timeline with the "OUT" frame label. When we hit the event hook AnimationEvent.ANIMATE_OUT, _onPreloaderOut is called:

private function _onPreloaderOut($evt:Event):void { // kill listeners. this will prep the loader for the next use. // if i didn't do this, I might get _onPreloaderIn() twice when I reach the ANIMATE_IN event hook. that would mess everything up. removeEventListener(AnimationEvent.ANIMATE_IN,_onPreloaderIn); removeEventListener(AnimationEvent.ANIMATE_OUT,_onPreloaderOut); if(_isLoadComplete) dispatchEvent(new Event(Event.COMPLETE)); else animateIn(); };

In the function above, I cleared the listeners out and dispatched the Event.COMPLETE event. Now any listeners in my program will know the PreloaderClip is finished and the Stage is clear to display the loaded content.

Taking the preloader for a test drive

I've got another class named Main, which will be my Document Root class. The Document Root is the main Timeline's class. When the SWF loads, the Document Root will be the controlling class. To set this, click anywhere on the Stage and then type the classpath to whatever class you want to use. In my case, I won't bury the Main class in a package; I'll simply call it Main and save my file to _classes/Main.as.

Let's take a look at Main:

public function Main() { Out.enableAllLevels(); Out.status(this,"Ready to go!"); // first let's start by setting our PreloaderClip to tell us when it's finished. remember, Main is our main Timeline and preloader_mc is a PreloaderClip. preloader_mc.animateIn(); preloader_mc.addEventListener(Event.INIT,_onPreloaderIn,false,0,true); preloader_mc.addEventListener(Event.COMPLETE,_onPreloaderOut,false,0,true); }; private function _onPreloaderIn($evt:Event):void { _loadCount = 0; // this is how to load SWFs, JPGs and such in AS3. _swfLoader = new Loader(); _swfLoader.contentLoaderInfo.addEventListener(Event.INIT,_onSWFLoaded,false,0,true); _swfLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,_onLoadProgress,false,0,true); _swfLoader.load(new URLRequest("loadedContent.swf")); };

When the constructor is called (that is, when the SWF initializes), I know I'll have a PreloaderClip on my main Timeline named preloader_mc. I'll tell preloader_mc to play its "IN" animation and have Main listen for when it is finished. Then I'll set up a Loader, which will do all the actual loading. The Loader will listen for load progress (ProgressEvent.PROGRESS), which will fire Main's _onLoadProgress function. The function looks like this:

private function _onLoadProgress($evt:Event):void { Out.status(this,"_onLoadProgress"); // this is all we need to display load progress. preloader_mc.updateProgress($evt.target.bytesLoaded,$evt.target.bytesTotal,_loadCount,_loadTotal); };

The value of _loadCount is 0 when loading the SWF and _loadTotal is 1. I know this because I'm only loading two files. Therefore, PreloaderClip will designate half of the actual progress to this SWF loading. When the SWF finishes loading, Main is ready and waiting with _onSWFLoaded. Here's what it looks like:

private function _onSWFLoaded($evt:Event):void { Out.status(this,"_onSWFLoaded"); _loadCount = 1; // so now we can start to load the xml. here's how in as3. // note that we can use the same _onProgress function for both SWF and XML. _xmlLoader = new URLLoader(); _xmlLoader.addEventListener(Event.COMPLETE,_onXMLLoaded,false,0,true); _xmlLoader.addEventListener(ProgressEvent.PROGRESS,_onLoadProgress,false,0,true); _xmlLoader.load(new URLRequest("content.xml")); };

Now I'm loading an XML file, named content.xml. Note that the XML Loader is calling the same method in Main to handle the progress: _onLoadProgress. One line of code in one function handles all the visual updating. The only difference now is that the value of _loadCount is 1 instead of 0. The progress movieclip is dedicated to 0–100% of total load.

When the XML file content.xml finishes loading, _onXMLLoaded is called. Here's what the function looks like:

private function _onXMLLoaded($evt:Event):void { preloader_mc.setComplete(); };

That's it. The preloader knows to finish the preloader and, as it animates out, Main is already listening for PreloaderClip.ANIMATE_OUT:

private function _onPreloaderOut($evt:Event):void { var loadedSWF:MovieClip = _swfLoader.contentLoaderInfo.content; addChild(loadedSWF); // the SWF is added to the stage. loadedSWF.gotoAndPlay("IN"); // finally something looks familiar, right? the loaded SWF will play it's in label. loadedSWF.addEventListener(AnimationEvent.ANIMATE_IN,_onLoadedSWFAnimateIn,false,0,true); };

The loaded SWF is added to the Stage and begins to play. That's all there is to it!

Bringing classes into Flash

How does Flash know where to find these external classes? By default, Flash looks in the base directory of your FLA file to find the classes. I personally dislike placing classes in this location. As a best practice, organize your classes into their own obvious directory. Here's how you can do that.

Select File > Publish Settings. Click the Flash tab and then click the Settings button next to the ActionScript version—which should be set to ActionScript 3, by the way (see Figure 2).

Upon clicking Settings, the ActionScript 3 Settings dialog box appears. Click the plus (+) button to add a new classpath. For this example, I'm going to put all of my classes in a directory relative to my FLA file named classes. So I'll type ./classes/ as the new classpath. (The trailing slash shouldn't matter.) After making these changes, the classpath is listed in my ActionScript 3 Settings (see Figure 3).

Figure 4 shows the (Mac OS) directory where my FLA is saved, so you can see where the _classes directory is located. All of my classes are stored in directories relative to the class package name. So if I have a class named foo.bar.MyClass, my class will be found at _classes/foo/bar/MyClass.as.

Generally it's considered a best practice to name classes beginning with the most general package name, and then make each subsequent directory more specific. I use com as the most specific class package possible. The directory bigspaceship contains the classes and packages built by/for Big Spaceship, frameworks contains all of the classes or packages that represent fundamental structures we use from project to project, and site contains classes specifically denoted for websites (as opposed to classes used for developing Adobe AIR applications or games). Of course, in this example all of the code is found in _classes/com/bigspaceship/frameworks/site/PreloaderClip.as.

Class package names are arbitrary; you can be as organized or disorganized as you like. When you work in a team environment, however, it's a good idea to adopt standards like this so that everyone on the team knows where to find the PreloaderClip. This ensures that your teammates don't waste energy making their own PreloaderClips. No sense reinventing the wheel, right?

Linkage

In ActionScript 2, linkage was all about identifying a unique ID. In ActionScript 3, linkage is similar but different.

In ActionScript 3, each clip has a unique linkage ID as before. But ActionScript 3 also includes an additional parameter: base class. The base class is whatever type this linkage is. In our case, because we're extending the MovieClip class, it's of the type flash.display.MovieClip. A full discussion of extending classes is beyond the scope of this tutorial, but be sure to check out the ActionScript 3.0 Language and Components Reference for more information on extending classes.

So in our example the preloader is of type com.bigspaceship.frameworks.utils.PreloaderClip. Its base class is flash.display.MovieClip. If I wanted to add the preloader to the Stage dynamically, I could simply add the following code:

import com.bigspaceship.frameworks.utils.PreloaderClip; var p:PreloaderClip = new PreloaderClip(); addChild(p);

This code creates a new PreloaderClip, which I'll reference via var p. Next I'll add it to the display list via addChild. You may be wondering, "What is the display list?" It's the new way that Flash Player handles objects (movie clips, bitmaps, buttons, etc.). It is rather complex and the full explanation of working with the display list is outside the scope of this article.

Loading at last!

At this point Flash knows that the clip I've placed on the Stage should be treated as a PreloaderClip, my custom class that extends the MovieClip class. I have my Document Root class all set up to leverage the PreloaderClip into displaying the progress. All I have left to do is compile the SWF file and relax.

Super modularity

There is one more issue I want to address. It is important to update the preloader so that it can accommodate the possibility of resetting the preloading process mid-load. I want to add the flexibility to cancel the preload animation at any time, in case the user decides to click a button and go somewhere else that requires some new and different content to be loaded.

To facilitate this functionality, PreloaderClip needs some sort of reset function to begin the load at 0%. Otherwise, the progress will not match the actual load, potentially confusing the end user into thinking that the site has stalled. I added this function to my PreloaderClip:

public function reset():void { _onProgressBarComplete(); _isLoadComplete = false; }; public function cancel():void { _onProgressBarComplete(); };

That it. We've built a preloader that is easy to reskin and easy to reuse. PreloaderClip should cover every situation I'll ever need a preloader for—or at least until the next version of ActionScript is released. In case you hadn't noticed, there isn't a single for loop or enter frame in the entire script. Avoiding the use of checking onEnterFrame and iteration loops will optimize your script's performance. As a result, this preloader will be lightning fast.

Where to go from here

The power of the IDE—and especially the Timeline—makes Flash a unique tool. Developers have been taught to shun the Timeline in favor of prettier, more technical code.

Developers: it's time to retrain your minds. The best solutions sometimes aren't the most technical or eloquent, but are the strategies that perform a precise role with as little reengineering as possible. Trusting and empowering designers to build to specification will generate a faster workflow, create less stress for future repurposing, and—most importantly—give you a better looking result. Keep that frame of mind as you create all of your work, even if you wear two hats and are responsible for both design and development.

Requirements

Prerequisite knowledge

This article covers the fundamentals of ActionScript 3. Some knowledge of ActionScript 2 is recommended but not necessary.

User level

Intermediate