Thursday, December 3, 2009

Preloading in ActionScript 3.0 Part 1

Exercise Files:
Preloader01_Start.fla
allin.swf

In this tutorial, we are going to learn how to create a preloader in Flash using ActionScript 3.

What is a preloader and what is it for?
Flash movies tend to have larger than average file sizes compared to HTML websites. So when you create a Flash website, chances are, it will take a while to load every time a user visits it. That's why it's important to give the user an indication regarding how much longer he or she has to wait. This can be done by adding a preloader to your Flash website. A preloader is simply a kind of visual feedback that shows the user how much of the Flash website has already been loaded. A simple preloader can show nothing more that just a basic progress bar and some text that displays the loading percentage, while other Flash websites will have fancier preloaders that contain more complex animation and design. Regardless of the complexity of the preloader that you choose to make, it's always a good idea to have one for your Flash website. Without a preloader, the visitor might just get a blank browser window at first and think that the website is broken. And instead of waiting for the site to load, the user will more likely end up leaving your website.

How can I create a preloader in Flash?
There are a couple of methods that you can use to create a preloader in Flash. The method that we will take a look at in this tutorial involves the use of 2 Flash movies. One would be your actual Flash movie or website, while the other Flash movie is the one that will preload the Flash website. So here, we have a dedicated Flash movie that will do the actual preloading.


What's going to happen is that the Flash website gets loaded into the preloader Flash movie.


As the Flash website gets loaded into the preloader Flash movie, the preloader Flash movie will calculate and display the loading progress. To do this, the preloader Flash movie needs to contain the ff:
  • the ActionScript code that does the preloading
  • the visual elements of the preloader (such as the progress bar and the percentage text field)


 This tutorial comes with 2 exercise files:
  1. Preloader01_Start.fla
    This will generate our preloader Flash movie. This is where we will put the ActionScript 3 code for preloading a Flash website or file.
  2. allin.swf
    This is the Flash movie that we will be preloading. It just contains a picture of some poker chips and some animated text that says "all in".

Make sure that you save both files in the same folder. Otherwise, the preloader Flash movie might not find the Flash movie that we want to preload.

Let's first take a look at the contents of our preloader Flash movie. So go ahead and open the Preloader01_Start.fla file. On the stage you will see a movie clip instance named progressBar_mc and a dynamic text field named percent_txt.


They will both be used to indicate the loading progress. The progress bar gets fuller as the loading progresses, and the text field shows the percentage value of how much has already been loaded.

If you look at the main timeline, you'll see that it has only one frame. But if you go inside the progress bar movie clip, you'll see that it contains some animation inside it. You can double-click on the progress bar movie clip to go inside its timeline.  

NOTE: If you have trouble selecting the progress bar, make sure that you click on the border.

Once you're inside the progress bar movie clip's timeline, you'll see that it contains a layer named bar that has a shape tween.


This shape tween shows the progress bar going from empty to full. You can test the movie to see how it looks like. But when you test it, you'll just see the progress bar animate and loop endlessly. You won't see the text field update itself, and you won't see anything load yet. That's because we haven't added any code.

This progress bar animation will be used to represent the loading progress. You'll notice that the animation inside the progress bar movie clip's timeline is made up of 100 frames.


This is not an arbitrary number. The animation really has to have 100 frames because we want it to represent 100%. Each frame of the animation, represents one percent of the file that we are going to preload. The great thing about this is that you can just replace the animation with something else if you want a different progress bar. Just make sure that it has 100 frames. It doesn't even have to be a progress bar. You can be more creative with your preloader animation if you want. But don't go too overboard with your preloader animation. If you do, then your preloader Flash movie might end up having such a large file size that it will end up needing its own preloader.

REMEMBER: The animation is INSIDE the progress bar movie clip's timeline, NOT on the main timeline.

So let's begin adding our ActionScript 3 preloader code. If you're still inside the timeline of the progress bar movie clip, make sure that you go back to the main timeline by clicking on the Scene 1 link.

Once you're back on the main timeline, select frame 1 of the Actions layer and then open up the Actions panel.

STEP 1

First, let's stop the progress bar animation. We don't want the progress bar to start moving right away. We only want it to start moving once the loading process has started. So make it stop using the stop() method.
progressBar_mc.stop();

STEP 2

Then create a Loader and a URLRequest object:
progressBar_mc.stop();

var myLoader:Loader = new Loader();
var myURL:URLRequest = new URLRequest("allin.swf");

What are these objects for?
Loader objects are used to load external SWF files into a Flash movie. So this Loader object is going to be responsible for loading the Flash movie that we want to preload. Without this object, then we would not be able to load our main Flash movie into our preloader Flash movie. In our example, the external file that we want to load would be allin.swf.

NOTE: Aside from being able to load SWF files, Loader objects also have the ability to load JPG, PNG, and GIF files.

The URLRequest object is used to specify the path to the external file that you would like to load. This is what will tell the Loader what it's supposed to load. In this example, we want to load the allin.swf file, so that's why we typed the file name inside the parentheses of the URLRequest() constructor.

NOTE: You must pass the file name or path to the URLRequest() constructor as a string. So it should be in quotation marks.

So to recap, the Loader object loads the file, while the URLRequest is used to specify the path to the file that needs to be loaded.

STEP 3

At this point, let's now load the external SWF file into our preloader flash movie. But before we continue, I must warn you: after we load the external SWF file, we won't see it yet. But that's ok. We'll fix that later on.

To load the external file, we use the load() method of the Loader class. The load() method needs an argument. It needs to know which file you want to load. We've already specified that when we created the URLRequest object. So we simply pass the URLRequest object as an argument to the load() method.

So let's go ahead and use myLoader (our Loader object) to load allin.swf (the external SWF file that we want to preload as specified in the myURL URLRequest object) using the load() method. Go back to the code and add the load() statement highlighted in bold:
progressBar_mc.stop();

var myLoader:Loader = new Loader();
var myURL:URLRequest = new URLRequest("allin.swf");

myLoader.load(myURL);

So this new line that we've added tells the myLoader object to begin loading the file requested by the myURL object (which is allin.swf). Without this line, then the loading will not start.

But remember, as I've mentioned earlier, when we test the movie, we won't see the file come out on the stage just yet. That's because we've told Flash to load it, but we haven't told Flash to display it yet. Loading and displaying are two different things. But don't worry, we'll fix that later on.

STEP 4

Next, we'll need some event handlers. These event handlers that we're about to add are events that relate to the loading progress and loading completion of our external file. These events are:
1. ProgressEvent.PROGRESS
This event refers to the progress of the loading process. It is active all throughout the time that the external file is being loaded. The event is dispatched every time new data from the external file is being loaded into the Flash document by the Loader.
2. Event.COMPLETE
This event is dispatched when the external file has successfully completed the loading process.

I'll explain what these event handlers will do later on, but for now, add the following code highlighted in bold:
progressBar_mc.stop();

var myLoader:Loader = new Loader();
var myURL:URLRequest = new URLRequest("allin.swf");

myLoader.load(myURL); 

myLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loading);
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);

function loading(e:ProgressEvent):void
{
//This will contain the formula that will calculate the loading progress
//as well as the code that will control the progress bar animation
//and the text field output
}

function loaded(e:Event):void
{
//Here, we will specify what will happen once the file has loaded successfully
}

NOTE: These two event listeners that I just mentioned are NOT added directly to the Loader object. You add them to the contentLoaderInfo property of the Loader object instead. So instead of typing myLoader.addEventListener(...), you'll type in myLoader.contentLoaderInfo.addEventListener(...).

STEP 5

Right now, we just created 2 empty event listener functions. One for ProgressEvent.PROGRESS, and another one for EVENT.Complete.

Let's go ahead and complete the loading function first. This function is the one that we assigned to the ProgressEvent.PROGRESS event, so this means that this function will get called numerous times - it will be called each time new data from the external file gets loaded into our preloader Flash movie. So basically, this event keeps happening as the loading progress occurs (hence the name ProgressEvent.PROGRESS). Because of that, we can use it to write some code that will calculate and update the user regarding the loading progress.

Go to the loading function, and add the following lines highlighted in bold:
function loading(e:ProgressEvent):void
{
var nPercent:Number = Math.round(e.bytesLoaded / e.bytesTotal * 100);
percent_txt.text = nPercent.toString() + "%";
progressBar_mc.gotoAndStop(nPercent);
}

So now we just added 3 new lines inside the loading function. Let's try to understand what these lines are for.

1st line: var nPercent:Number = Math.round(e.bytesLoaded / e.bytesTotal * 100);
The first line calculates for the loading percentage. In order to calculate for the loading percentage, you will need to get the amount of bytes that have already been loaded, and divide it by the external file's total number of bytes. And then we multiply that value by 100 to convert the value into percent.

So how do I get the number of bytes that have been loaded and the total number of bytes?
This information automatically gets passed to the ProgressEvent.PROGRESS listener function (which in this case is our loading function). The number of bytes that have already been loaded can be retrieved using the bytesLoaded property, while the file's total number of bytes can be retrieved using the bytesTotal property. To access these properties, we use our event object - e:

e.bytesLoaded - the number of bytes that have been loaded
e.bytesTotal - the external file's total number of bytes (or its total file size)

Then divide those two values and multiply by 100 to get the percentage value.
e.bytesLoaded / e.bytesTotal * 100;

Then use Math.round() in order to round the value to a whole number.
Math.round(e.bytesLoaded / e.bytesTotal * 100);

Then we assign the equation to a variable (which I named nPercent):
var nPercent:Number = Math.round(e.bytesLoaded / e.bytesTotal * 100);

And that is what the first line in the loading function means.

We will then use this nPercent variable for 2 things: (1) to display the percentage value in the text field and (2) to control the animation of the progress bar.

2nd line: percent_txt.text = nPercent.toString() + "%";
The second line in the loading function displays the value of nPercent in the dynamic text field on the stage. I've named the dynamic text field percent_txt. The text property of the TextField class lets you assign the text that you want to appear in the text field. So we assign the nPercent variable to it in order to display its value in the text field. However, nPercent is of the Number data type. Text fields can only display strings so we simply convert nPercent to the String data type using the toString() method. If you want the number to appear with a percent sign, then simply concatenate the value with the percent(%) character.

NOTE: The toString() method will not convert the numbers into letters. For example, 4 will not be converted to four. It will still be displayed as 4 but will be treated as a String instead of a Number. We need to do that so that the text field will accept it.

3rd line: progressBar_mc.gotoAndStop(nPercent);
The last line in the loading function controls the timeline of the progressBar_mc MovieClip. Recall that there is a 100 frame animation inside this MovieClip. On frame 1, the progress bar is empty. As the animation progresses, then the progress bar fills up until it is completely filled at frame 100. There's a reason why we want the animation to have 100 frames. We want to associate each frame number with the percentage of data that has been loaded. If 20% of the external file has been loaded, then we want the playhead to move to frame 20 of the progress bar animation. If 50% of the data has been loaded, then we want the playhead to move to frame 50, and so on... In order to do that, we can use the gotoAndStop() method of the MovieClip class and pass nPercent to it. So as nPercent increases, then the animation moves forward in sync with the percentage value.

NOTE: It is important that nPercent is rounded to a whole number because of this line. That's why we used Math.round() to round it. We cannot tell Flash to go to a frame no. 25.43, for example, since there is no frame no. 25.43. Frame numbers are always whole numbers.

And that completes our loading function. In the next step, we'll test the movie, but we'll simulate the download process when we do the testing. This means we'll make the movie behave as if we're actually viewing it online, so instead of finishing the loading process right away, we'll wait a while as if we were actually viewing it online. We want to simulate the download so that we can actually see the progress bar update. If we didn't simulate the download, then everything will just load right away.

STEP 6

How do we simulate the download?
First, you must test the movie. The keyboard shortcut for testing the movie is ctrl + enter (Windows) or cmd + return (Mac).

Once you test the movie, you should see the progress indicators go to 100% right away. This means that the external SWF file has already been loaded (but remember, you won't see it yet!). That was pretty fast! We just tested the movie, and all of sudden it loads right away. That's because our files are just in our hard drive. So let's change a few settings that will make Flash pretend as if we were testing this online instead.

So while the test movie window is still there, go to the View menu. If you're on a Mac, the View menu can be found in the menu bar at the top of your Flash workspace. If you're on Windows, the View menu can be found at the top of the actual test movie window.

So go to View, and then go to Download Settings, and then choose the desired download speed. Let's choose the 56K speed. This means that when we test the movie, Flash is going to behave as if we were viewing it online using a 56K connection. This is a pretty slow connection, so our small external SWF file should take about 7 to 8 seconds to load.

Now that we've chosen a speed, go to View again, and then choose Simulate Download. This will test the movie again, but will behave as if we we're viewing it with a 56K connection. This time, you should now see the progress bar update.

At this point, you still won't see the external SWF file appear on the stage. But it is being loaded. We'll be fixing the code soon so that we actually get to see the allin.swf file once the loading completes.

STEP 7

Let's now go to the loaded function and put some code inside it. The loaded function is the listener function that we assigned to  EVENT.Complete. This means that this function will get called, once myLoader finishes loading the external SWF file. So whatever it is that we want to happen once the loading is complete, we should place in this function.

One of the first things we want to do is to remove the progress bar and text field from the stage. Some people might want to leave these visible, but I prefer to remove them so the stage won't be so cluttered.

So go to the loaded function, and add the following lines:
function loaded(e:Event):void
{
removeChild(progressBar_mc);
removeChild(percent_txt);
}

Once the loading is complete, these 2 lines will remove the progressBar_mc and percent_txt display objects from the stage.

NOTE: Another option would be to use the visible property:
progressBar_mc.visible = false;
percent_txt.visible = false;

STEP 8

So now, test the movie again. Be sure to simulate the download. You should see the progress bar and text field disapper once it gets to 100%.

STEP 9

So now, let's go ahead and make sure that we actually see the externally loaded SWF file once the loading is complete. To do that, we need to add it to the display list. If we can remove things from the display list using removeChild(), then we can add things to the display list using addChild(). When you add something to the display list, it means that the display object can now be seen.

So go back to the loaded function and add the following line highlighted in bold:
function loaded(e:Event):void
{
removeChild(progressBar_mc);
removeChild(percent_txt);
addChild(myLoader);
}

So here, we are adding myLoader to the display list. We should now see the externally loaded SWF file show up on the stage once the loading is complete.

But why are we adding myLoader? Shouldn't we add allin.swf?
allin.swf is actually inside myLoader. When a Loader object loads an external file, the external file is actually loaded inside the Loader object itself. So the external file becomes a child of the Loader object that loaded it. So if you add the Loader to the display list, then its child gets added as well.

STEP 10

So now, go ahead and test the movie. Be sure to simulate the download as well. And you should now see the externally loaded SWF file appear on stage once the loading is complete.

STEP 11

Lastly, I want to remove a few more things once the loading is complete. I want to remove the event listeners that we created. So go back inside the loaded function and add removeEventListener() statements for the ProgressEvent.PROGRESS and Event.COMPLETE listeners.

function loaded(e:Event):void
{
removeChild(progressBar_mc);
removeChild(percent_txt);
addChild(myLoader);
myLoader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, loading);
myLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaded);
}

Why are we removing them?
In this case, it's because we no longer need them once the loading is complete. It's a good habit to remove listeners that you no longer need. It might help you save more computing resources in the long run, especially if you have a pretty heavy Flash movie. But if your Flash movie needs to preload something again, you might need to keep the event listeners available instead of removing them. Or you can also just add them again at some point in your code. Likewise, you should add the progressBar_mc and percent_txt display objects back to the stage if your Flash movie needs to preload another external SWF file. If you try to remove something that's already been removed using removeChild(), then you'll get an error message. So be sure to add those back if you need them again. But in this example, we're only loading one external SWF file, so there won't be a need for that.

NOTE: Each Loader object can only load an external file one at a time. If you want to load multiple external files all at once, then you should create multiple Loader objects.

And that completes our preloader. Here's the full code:
progressBar_mc.stop();

var myLoader:Loader = new Loader();
var myURL:URLRequest = new URLRequest("allin.swf");

myLoader.load(myURL); 

myLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loading);
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);

function loading(e:ProgressEvent):void
{
var nPercent:Number = Math.round(e.bytesLoaded / e.bytesTotal * 100);
percent_txt.text = nPercent.toString() + "%";
progressBar_mc.gotoAndStop(nPercent);
}

function loaded(e:Event):void
{
removeChild(progressBar_mc);
removeChild(percent_txt);
addChild(myLoader);
myLoader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, loading);
myLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaded);
}

Go to Part 2 (Preloading in ActionScript 3.0)