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:

onClipEvent (load) {
    _parent.stop();
}

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

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:

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

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

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:

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

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

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

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

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:

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;
}

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:

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

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:

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();
                        ????????? }
                ????}
}

"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.