ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources
Yet Another Flash MX Loader
http://www.actionscript.org/resources/articles/165/1/Yet-Another-Flash-MX-Loader/Page1.html
Tim Murray
This user is yet to take control of their account and provide a biography. If you are the author of this article, please contact us via support AT actionscript DOT org. 
By Tim Murray
Published on September 9, 2005
 
Tutorial details: We show you how to plan, design, program, and use the most flexible Flash MX preloader (or loader, depending on how you want to use it). This is for the designers out there who want a better than average loader that allows some creative flexibility in its presentation.
Written by: Tim Murray [email:tim.murray@gmg.com.au], www.gmg.com.au
Difficulty Level: Advanced
Requirements: Flash MX, some largish images and sound files
Topics Covered: Designing, coding, testing, shared resources, getBytes commands, _parent object
Assumed Knowledge: Basic math, basic ActionScript, interface familiarity

Page 1 of 5
Tutorial details: We show you how to plan, design, program, and use the most flexible Flash MX preloader (or loader, depending on how you want to use it). This is for the designers out there who want a better than average loader that allows some creative flexibility in its presentation.
Written by: Tim Murray [email:tim.murray@gmg.com.au], www.gmg.com.au
Difficulty Level: Advanced
Requirements: Flash MX, some largish images and sound files
Topics Covered: Designing, coding, testing, shared resources, getBytes commands, _parent object
Assumed Knowledge: Basic math, basic ActionScript, interface familiarity

Yet Another Flash MX Loader

Introduction

What the world needs now is certainly not another Flash MX preloader. Unless it is accompanied by a great tutorial on not only what to do but also why we should do it! This is for the designers out there who want a better than average loader that allows some creative flexibility in its presentation.

We will be developing this Flash MX file: loader.fla (35kb).

There are heaps of loaders freely available that do most of what most people need. In this tutorial we are going to start at the beginning and try to design a stylish preloader from the ground up while keeping in mind some technical constraints such as load time, ease of implementation, and code and resource reuse.

Some of the major benefits of a well designed Flash loader are:

  • It can provide users with imme diate feedback that their command to view or play something has been accepted.
  • It keeps users informed of download status so that they know that something is happening.
  • Loaders can keep people entertained while the file request is met.
  • People with broadband connections are probably downloading lots of stuff simultaneously (we do), so they still want feedback.

Before we go any further, I would like to point out that some of the screen shots are from the Western Australian Museum's "Western Australia: Land and People" exhibition. Their great content and our stylish Flash MX work produced an inspiring virtual exhibition.

I am assuming that you have had a glance through the Using Flash help file, and that you have completed the online Flash tutorials "Introduction to Flash MX Tutorial" and "Introduction to ActionScript Tutorial" that come with your copy of Flash MX.


Page 2 of 5

What should the loader do?

Okay. Here's the easy part. Get out a pen and paper and write down the things that you would like your loader to do and be. Go on. I'll wait.

Done that? Good. Now sort them in order of importance. This is so that you get the most pressing things designed, coded and tested first, leaving the cute bits for a later revision.

Here's my list:

  1. Be small.
    So that we don't wait forever for the preloader to load, requiring its own loader. (Which would then require another preloader for that, and... aargh ? where are my pills?)
  2. Display a progress bar to let the user know how the download is going.
    This is why we are here. Since Flash MX is a visual tool, let's use it. A progress bar gives the quickest indication of the status of a command.
  3. Give them details (downloaded amount and the total bytes).
    This is good for your more advanced surfers. It lets them guesstimate progress for themselves. As a bonus, newbies can also learn how long things take.
  4. Provide a loading animation.
    Loading animations are especially good for long tasks like loading large files because they let the user know that something is happening and their computer hasn't crashed.
  5. Be easy to reuse in several SFW files.
    We don't want all our good efforts to be wasted, do we?
  6. Stops the file from playing until the content is loaded.
    Let's try to make the loader do all the work for us.
  7. Be quick to add to a file.
    Because I'm busy.
  8. Be simple to add to a file.
    Because I forget stuff.
  9. Not have to be downloaded every time.
    So that we don't clog our bandwidth with loaders!
  10. Detects if a file has already been loaded.
    Giving us a chance to take action if this is the case.
  11. Gives an estimated time to completion.
    Another method of giving the progress. Use it depending on the target audience. In my humble opinion this is not as reliable as bytes downloaded, progress bars or percent downloaded because it may be affected by other bandwidth hogs that may be on the machine. If your loader estimates that the file will be down in 2 minutes, but actually takes 4 minutes because email starts downloading as well, then the user's expectations of 2 minutes aren't met. They get grumpy and start looking for other faults.
  12. Waits for a certain percentage to load before playing.
    This lets us download an appropriate amount of content before playing the file, giving us a capacity to stream our content (playing the file before all of it is loaded ? one of the best features of Flash MX).
  13. Plays a quick animation even if the file has already been loaded.
    This will give a consistent feel to loading content. Why do some things have loaders while others don't? Users have expectations of interacting with software. If users see a loader the first time, then they should see it every time. (Just make it quick so that it doesn't irritate them.)
  14. Be easy to change the loading animation.
    So that we can change the feel of a loader for a new situation, client, or project. If it is hard, we will probably try to not do it.

The requirements for a particular loader that you build will have differing priorities depending on client, anticipated user groups, style, and budget. But this list should give you a feel for what we are going to do.

How should the preloader work?

We are going to create a runtime shared resource that we can link to from our other Flash MX movies. Movie reusability in Flash? You betcha!

This is a SWF file that has a loader movie resource containing load animations, a progress bar, and some custom smarts allowing us to quickly work out how our preload is going.

On this side we have the SWF files that require a loader. We will be dragging and dropping our loader from our resource library on to the frames where we want the loader to appear. We will then add some load and enterFrame clip event code to finish them off when we use them.

What should it look like?

Now, out with your crayons and butchers' paper and come up with something that looks good and will be a small file and animates well. Here's our design for the Western Australia: Land and People exhibition created for the Western Australian Museum:

Here we have used the loader after animating in the small elements (window, caption, title bar graphics, and text). This gives the user an entertaining build to the window (window elements stamp or fade in to place) leaving them with the text to read while the image downloads. Instead of leaving a blank window, the loader appears giving them an indication of how the download is progressing.

Here is the result, the wonderful life-size Carnotaurus sastrei on display in the Western Australian Museum's Perth exhibition site:


Page 3 of 5

Useful reading

Our preloader will be a self contained movie with a bit of code on it. This will help us to encapsulate our functionality into a library resource that we can share between Flash MX files so that we don't have to download our loader over and over again.

Now would probably be a good time to have a quick look at "shared libraries" entry in the index of your online Flash MX documentation. The page title is "Using shared library assets". You will also find it in the "Using Flash > Using Symbols, Instances, and Library Assets > Using shared library assets" in the contents of your online help.

You can also create shared library assets for fonts. See "Using Flash > Working with Text > Creating font symbols" in your help file for more info.

Designing the loader movie

The key to any Flash MX development is to know when to code and when to animate. Anything that can be quickly accomplished using the timeline, stage, movies and tweening should probably not be coded. Flash MX is great for this, it optimises well and runs fast, and the visual tools within Flash MX make for a rapid development cycle.

Our approach will be to do the graphic design work first, getting the feeling and style of what we want. Then we will incrementally build the code to support our preloader, so that we can test as we go along.

Start Flash MX and create a new file. Save it and call it something sensible. We picked "loader.fla" for want of a better name.

So, create a movie clip symbol, name it appropriately. We called it "loading" 'cos that's what it's going to say when we're finished. If you don't have the "Advanced" part of the dialog available, click on the "Advanced" button now. We will make sure that we export the movie for runtime sharing (so that we can use our loading movie in other Flash MX files once our primary Flash file, in this case "loader", is loaded), but don't bother with exporting the resource in the first frame (as this will cause an unnecessary delay while our loader is loaded for the first time in our main Flash file).

Now we go into our loading movie and create our loading animation using the design tools within Flash MX.

Our functions layer contains all of the ActionScript functions that our loader will need. We'll get to the scripting shortly.

The file info layer contains our dynamic text object that we will use to display our bytes loaded info. This will be handled from the scripting using a variable named "loadedText". We let Flash know this by typing in the name of the variable that we are going to use in the "Var" field in the Properties window.

The text layer contains the word "LOADING", and the shadows layer contains all of the pale grey squares that act as placeholders for the green boxes that we will be animating in as the file loads.

The remaining layers (creatively named Layer 3 to 15) contain our tweened box shapes. We created a layer, made a box shape, and duplicated the layer the appropriate number of times. Then we dragged the boxes on each layer where we wanted them and created the key frames by multi selecting the frames where we wanted the keys. Selecting all of the first key frames, we set a shape tween for all of our boxes then set the alpha to 0%. Finally we staggered the key frames out so that the boxes animated in one at a time.

Your loader movie may look different at this point (and rightly so), but the key parts that should be the same are the functions layer (where we will put some ActionScript soon) and the Dynamic Text object with the Var field set to loadedText (where we will place the bytes loaded feedback for the user). And of course, for more interest, you could always put extra movies in your loader (say, for a spinning 3D logo, flashing "LOADING" text, or whatever else takes your fancy). Just don't get too carried away!

Save it at this point.


Page 4 of 5

What have we got so far?

You should have a file (in our case "loader") with a movie in it ("loading") that is ready to be shared out to other Flash MX files.

If you preview your working file at this point you will see your loading animation looping on the stage.

The way we have designed things, we have met requirements 1, 4, 5, 9, and 14. And we have nearly achieved 2, 3, 6, 10, 12, and 13. Seven and eight will need to be proven, and I will probably end up leaving 11 as an exercise because I believe that this feature isn't actually useful most of the time.

Depending on how you set up your fonts, you can range from 2kb (using a _sans font) for your loader to around 6kb (using Arial and embedding font outlines for 0-9, k, b, o, and f in the Dynamic Text object). If your project warrants it, you can also create a shared library asset of your font of choice. See "Using Flash > Working with Text > Creating font symbols" in your help file for more info.

Getting it to stop and play its parent

Our loading movie will need to respond to load and enterFrame movie clip events. In the load event we will carry out some initialisation and in the enterFrame event we will check to see how our loading is going.

At this point it is probably useful to create a test bed movie to see how we are doing. This may be done with your original file or do a "Save As..." and name it something like "test-bed.fla". We will put the preloader movie on a layer named loader and place it only in the first frame. Create another layer that contains a large image (as we have done in the image layer below) and have it appear in frames 2 and 3. Also make a actions layer for assorted ActionScript and give that a keyframe in frame 3. Put a stop() command in that frame.

If you preview your test file (Ctrl+Enter), you should only see your image. Your preloader will just appear to be skipped.

Now close your test window and go back to your development windows. Go to frame 1 and select your loader movie. In the "Actions ? Movie Clip" window, make sure you are in Expert Mode.

The first code to add will make the parent movie (the movie that we are loading) stop and play automatically. Let's add that code:

[as]onClipEvent (load) {
    _parent.stop();
}

onClipEvent (enterFrame) {
    if (_parent.getBytesLoaded() >= _parent.getBytesTotal()) {
        ? _parent.play();
    }
}[/as]

First, the loader stops the parent from playing, then every frame it checks to see how the download is going. If it has loaded completely, the parent movie is allowed to play.

If you preview this, you won't see any difference unless you Show Streaming (Ctrl+Enter in the preview window). When you stream the content however, the parent movie will stop and wait for the whole file to load before continuing on.

For a better test, add a large sound file to the stage. Add a large sound sample to your library (if it is too small, your sound won't have the chance to stream properly for this test). Create another layer named "sound" and add a keyframe to frame 2. Now make your sound stream by adding it to your sound layer at frame 2, ensuring that you have enough frames to fully play your sound. Finally, drag your stop script and your image to display after your sound has finished streaming.

When you preview this with Show Streaming on, your loader will loop its animation until the whole file is loaded, then the movie will continue playing to the end. If you go back and remove the code from your loader movie in frame 1, you should see the loader briefly flash on the stage, then the play head stops until enough sound is loaded to play the first part of the sound. As the file continues to load, your sound will stream until it completes playing then stop again while the image finishes loading.

Here's our Flash MX test file: test-bed.fla (1.7Mb). (It's big 'cos it includes the sound and image files - uncompressed!)

Save your file at this point.

Setting a preload amount

Now, we don't always want to load the whole file before playing the movie (if fact loading it all is usually the exception ? not the rule). So let's define what percentage we want to pre-load before we start playing the movie again.

Here's the code:

[as]onClipEvent (load) {
        ????preLoad = (_parent.getBytesTotal() * 0.75);? //percent to preload
        ????_parent.stop();
}

onClipEvent (enterFrame) {
        ????if (_parent.getBytesLoaded() >= preLoad) {
                ????????? _parent.play();
                ????}
}
[/as]

preLoad is a variable that is attached to our loader movie. It contains the number of bytes that we want to load before playing the parent movie. I picked a value of 75% for starters. It can be changed at any time as required. We set it in the load event of the loader so that it is computed only once because function calls (such as the getBytesLoaded function) and instructions to multiply numbers can slow down your ActionScript code ? especially if called every frame.

The enterFrame event now gets the number of bytes loaded so far and compares it to the amount that we want to pre-load. If we have enough, it starts the parent movie playing again.

Sync the loader movie with the download amount

Okay. So we have it starting and stopping without much effort from us and it displays our looping loader movie. Great for some preloaders but we have bigger plans.

We are now going to add a function to our movie that tells us what frame we should be displaying when a certain amount of the file has downloaded. We will move the playhead of our loader movie to a point that corresponds to the percent downloaded. The function is hidden in the loader movie because once we have finished with it, we won't need to change it unless we are changing our loader animation.

Let's do it. Open your loader movie and click in your functions layer. The code to add is:

[as]function loadedIndicatorFrame() {
        ????var newFrame = int((_parent.getBytesLoaded() / _parent.getBytesTotal()) * 65) + 2;
        ????return newFrame;
}
[/as]

And back in our test movie on our loader movie clip we now have:

[as]onClipEvent (load) {
        ????preLoad = (_parent.getBytesTotal() * 0.75);? //percent to preload
        ????_parent.stop();
}

onClipEvent (enterFrame) {
        ????gotoAndStop(loadedIndicatorFrame());
        ????if (_parent.getBytesLoaded() >= preLoad) {
                ????????? _parent.play();
                ????}
}
[/as]

The function calculates the fraction of the parent movie that has been downloaded, stretches it over the 65 frames that I have as my progress bar animation (ranging from frame 2 to 67), and adds in an offset of 2 (for frame 2 ? which is where my animation starts). That value is then returned to the calling script and is used in a gotoAndStop function call for our loader clip.

The enterFrame clip event has been changed to move the playhead of my loader movie to the frame that corresponds to the amount of the parent movie that has been downloaded.

Alright, done! Stream-preview your loader (Ctrl+Enter, Ctrl+Enter). The loader animation should crawl along with the download.

Adding text feedback

Time to add some text feedback. Given that we have all the info in our loadedIndicatorFrame function, I'm going to break a programming rule that says I should put the update text commands in its own function. My way will give us a slightly smaller loader and the script is small enough to not cause too many problems.

Here's our new function:

[as]function loadedIndicatorFrame() {
        ????var newFrame = int((_parent.getBytesLoaded() / _parent.getBytesTotal()) * 65) + 2;
        ????loadedText = int(_parent.getBytesLoaded() / 1024) + "kb of " + int(_parent.getBytesTotal() / 1024) + "kb";
        ????return newFrame;
}
[/as]

No other changes here. Time to test it. You should see the numbers ticking up until the parent movie starts to play.

Divide bytes by 1024 to get kb. The int function gets rid of all the screwy decimal places caused by the division.

Now how does that get into the text box in our loader? Remember that we set the Var field for the Dynamic Text object to be loadedText? That creates a variable called loadedText and links it to the Dynamic Text object so that any changes to the variable will appear in the object automagically.

By now, we have met requirements 1-6, 9, 12, and 14. We only have 10 and 13 to go. Seven and eight are still debatable, and I will leave 11 as an exercise (have a look at the getTimer function for some inspiration), but I still think it's a waste of time.

Setting a maximum loader play rate

I worked out that I needed to control the rate of playback of the loader after I had built one. If the movie had already been loaded, the loader would give an annoying flash before the content appeared on slower machines (tip: always have some outdated boxes in your office for testing). Also, I wanted the loader to play through quickly every time, just for effect, even if the movie had already downloaded and was in the cache.

This requires two things: we need to know if the movie that we are loading has already been downloaded to the user's computer, and if that is the case we need to set a maximum frame rate for playing our loader before restarting the parent movie.

The first bit is easy. Just check to see if the bytes loaded equals the total bytes of the parent movie. If so, we set a flag (I've called it "quickPlay") to true so that we don't have to check every frame. Otherwise we are interested in finding out how much pre-load we need to do, as before. Here's the code for the load event that sits on our loader movie:

[as]onClipEvent (load) {
        ????if (_parent.getBytesTotal() == _parent.getBytesLoaded()) {
                ????????? quickPlay = true;
                ????} else {
                ????????? preLoad = (_parent.getBytesTotal() * 0.75);? //percent to preload
                ????}
        ????_parent.stop();
}
[/as]

Now we need to tweak our enterFrame event handler to support this new condition. Given that we will somehow be playing our loader movie at a maximum speed, we will need a different test when we are playing the loader quickly because all of the bytes will already be loaded. We will do this be seeing if the playhead of our loader has reached the end of its animation. And here's the code for that:

[as]onClipEvent (enterFrame) {
        ????gotoAndStop(loadedIndicatorFrame());
        ????if (quickPlay == true) {? //quickly play the anim
                ????????? if (_currentframe == _totalframes) {
                        ?????????????? _parent.play();
                        ????????? }
                ????} else {? //wait for the preload
                ????????? if (_parent.getBytesLoaded() >= preLoad) {
                        ?????????????? _parent.play();
                        ????????? }
                ????}
}
[/as]

"Now hang on a minute!" I hear you say. "Why don't you set quickPlay to false if have to wait for the movie to load? That's a sloppy bit of coding there!" Well, if we don't set quickPlay to true, when we do the test if (quickPlay == true) quickPlay will evaluate to "undefined" which certainly is not equal to "true", so the code works fine. And we save ourselves some bytes by not writing quickPlay = false; in the else clause in the load event handler.

There's no real point in testing this code now because our loadedIndicatorFrame function will just jump straight to the end. But if you are really keen, you can remove the function entirely to see what happens.

Doing a plain vanilla preview (Ctrl+Enter) now will allow your loader animation to play through once before continuing on with the parent movie. When previewing like this it is just like having already downloaded the entire movie, so this is doing our quickPlay bits of code. When streaming the preview (Ctrl+Enter again), you should see your loader animation loop until you hit the required percentage, then it will continue on playing the parent movie to the end.


Page 5 of 5

Setting a maximum load rate

The last bit of code will limit the number of frames that we can leap in a single bound. All we have to do is keep track of what frame we handed back on our last function call, compare it to where we want to go now, and if it is too much of an advance then we clamp it to a maximum jump.

To achieve this, we will need an extra variable named lastFrame to store the last frame we showed. This is initialised to 1 which is the frame just before our loader animation starts. Here is the final loader code:

[as]lastFrame = 1;


function loadedIndicatorFrame() {
        ????var newFrame = int((_parent.getBytesLoaded() / _parent.getBytesTotal()) * 65) + 2;
        ????if (newFrame - lastFrame > 4) {? //too far
                ????????? lastFrame += 4;
                ????????? loadedText = int(_parent.getBytesTotal() / 1024 * (lastFrame - 2) / 65) + "kb of " + int(_parent.getBytesTotal() / 1024) + "kb";
                ????} else if (newFrame - lastFrame > 0) {? //normal move
                ????????? lastFrame++;
                ????????? loadedText = int(_parent.getBytesLoaded() / 1024) + "kb of " + int(_parent.getBytesTotal() / 1024) + "kb";
                ????}
        ????return lastFrame;
}
[/as]

Most of the code is the same, but I'll give it a quick run through anyway. Given that our frame rate for the Western Australian Museum project was set to 25 fps, we found that skipping 4 frames at a time gave us enough time to see the loader and still not be annoying if a movie was already downloaded. Just play with the values to get it right for your frame rate.

If we are skipping frames for a movie that has already been downloaded, then we want to fake our bytes loaded text so that it matches the current frame that we are showing. Our loadedText calculations are now based on lastFrame instead of getBytesLoaded.

Preview now (Ctrl+Enter) to see your loader animation play through once in a fast-forward style before continuing on with the parent movie. When streaming the preview, you should see your loader animation tick through according to your bandwidth settings until you hit the required percentage, then it will continue on playing the parent movie to the end.

If you find that you are creating large SWF files (such as for video) you might find that you need to update the Dynamic Text object more frequently than when you advance to the next frame of your loader. The code for this would be:

[as]lastFrame = 1;


function loadedIndicatorFrame() {
        ????var newFrame = int((_parent.getBytesLoaded() / _parent.getBytesTotal()) * 65) + 2;
        ????if (newFrame - lastFrame > 4) {? //too far
                ????????lastFrame += 4;
                ????????loadedText = int(_parent.getBytesTotal() / 1024 * (lastFrame - 2) / 65) + "kb of " + int(_parent.getBytesTotal() / 1024) + "kb";
                ????} else if (newFrame - lastFrame > 0) {? //normal move
                ???????lastFrame++;
                ????????loadedText = int(_parent.getBytesLoaded() / 1024) + "kb of " + int(_parent.getBytesTotal() / 1024) + "kb";
                ????} else {? //update the text only
                ????????loadedText = int(_parent.getBytesLoaded() / 1024) + "kb of " + int(_parent.getBytesTotal() / 1024) + "kb";
                ????}
        ????return lastFrame;
}
[/as]

Now would be a good time to save this using Save As... so that you can give your loader a better name (choose "loader" again). It would also be good to get rid of any test images, sounds, and layers from your file. After tidying up the file, check the linkage on your loading movie in the Library for loader.fla by right-clicking on it and selecting the Linkage... option. It should look like this:

Save it, publish it, and we're ready to rock and roll.

Using the loader

All right, let's see how easy this sucker is to use!

Create a new file and open up your Library window (leave your loader.fla file open as well).

Drag your loading resource from loader.fla to your new movie. All the linkage will be set up correctly for you, but you can check it if you want. You should see that it has been set up to "Import for runtime sharing" from loader.swf.

Create some layers and add resources like you did for the test-bed.fla movie. Make sure you put a stop script in the last frame. I ended up with this:

Now go copy the code from your loader resource and paste it on to your loader in your new movie. Change your preload percent if you want.

That's it! Save and publish. Nothing more to do.

Conclusion

In building the virtual exhibition for the Western Australian Museum, we made extensive use of the loader. With over 360 SWF files to load we needed a resource that would not be a large overhead on the bandwidth, would be easy to add to a movie, and could handle custom preload amounts.

The way we approached most of our files was to load all window, caption, and text elements as early as we could, then when we hit the larger elements, like images and sounds, we would use the loader to indicate progress. Because we can put it anywhere we like, we can make the best use of the streaming capabilities of Flash MX and just when things start to stall (when it hits a large image or audio sample) we whack in a loader so that the site visitor still sees something happening.

Having a loader does not mean that you can get away with developing bloated Flash MX sites: great big monolithic monsters have gone the way of the dinosaurs. Be clever and stream as much content you can in an entertaining way. At some point, though, you will probably need a loader when the media you want to show gets too big. This approach should serve well for most occasions.

About the authors

The Glasson Murray Group, Pty. Ltd. creates and presents high quality and engaging content for delivery across a range of media. They designed and developed the virtual exhibition in conjunction with the Western Australian Museum, producing a truly compelling and unparalleled presentation.

http://www.gmg.com.au/

Copyright

Materials are copyrighted and are protected by worldwide copyright laws and treaty provisions. Copyright law in Australia is contained in the Copyright Act 1968 (Cth) and in decisions of courts, with further amendments in the Copyright Amendment (Moral Rights) Act 2000. They may not be copied, reproduced, modified, published, uploaded, posted, transmitted, or distributed in any way, without GMG's prior written permission. Except as expressly provided herein, GMG and its suppliers do not grant any express or implied right to you under any patents, copyrights, trademarks, or trade secret information. Other rights may be granted to you by GMG in writing or incorporated elsewhere in the Materials.

? 2003 Glasson Murray Group Pty Ltd (ACN 098 651 542), Western Australia. All rights reserved.