Archive for August 12th, 2010

In Flash Professional CS5, when you use TLF text in your SWF, by default we do some tricks behind the scenes to preload TLF as a Runtime Shared Library (RSL) in a way that does not require you, the content author, to write any ActionScript to manage the preloading at all. However, if you are setting up your FLA with preloading in mind, where you use the first one or more frames as a preloading animation and then have ActionScript to move past this section once the loading is complete, then you should take advantage of the custom preloader loop feature which will work with your preloader animation and will only require a tiny bit of extra ActionScript. And in fact if you do not use the custom preloader loop feature, then your preloader animation will never be displayed.

General Preloading Example

Let’s walk through an example first and then I’ll go into some more explanantion of what is really going on. Flash CS5 has a sample template installed called Preloader for SWF. You can create a new FLA from this template by selecting the File -> New… menu item, then selecting the Templates tab, selecting the Sample Files category and finally select the Preloader for SWF template and click OK. This template is set up with some loading text on frame one along with some ActionScript on frame one to track the loading and display a percentage to the user until 100% is loaded, at which point it will go to frame two which has the real content.

To see how this preloader template is intended to work, select frame two of the Content layer in the Timeline panel and import some image. Next select the Control -> Test Movie menu item and you should see it go to your content on frame two seemingly instantly, which is because of course it did not have to download. If you keep the test movie window open and select View -> Simulate Download, you should now see the LOADING text and the download percentage display.

Adding TLF Breaks the Preloading Animation

Now try this: move the image off to the side, so it isn’t covering the “Large content here” text. Select that text and in the Properties panel change the combobox at the top from Classic Text to TLF Text. Now try the same steps again. First, when you test movie, you may get a warning dialog like this:

Your content will not stream. Runtime Shared Library (RSL) preloading will require all of your content to download before the first frame will play.  To prevent this you can change the Runtime Shared Library Settings in the Advanced ActionScript 3.0 Settings dialog which can be raised from the Publish Settings dialog.  The Runtime Shared Libraries being preloaded are:  textLayout_1.0.0.595.swz for TLF Text

So let’s talk briefly about what this dialog is saying. A preloader animation loop at the beginning of your timeline works in Flash because the player will start playing frames as soon as they are downloaded. This is what this dialog’s text means by “streaming”. So your preloader code and animation defined in frame one starts running as soon as all of frame one has downloaded, and while the user waits for the rest of your SWF to download, the preloader animation will run. However, the tricks we use to preload the TLF RSL before your content runs defeat this strategy. They require your entire SWF to download completely before frame one can be run. This is why it is so important to take advantage of the custom preloader loop which preserves your ability to stream your SWF and allows you complete control over the preloading experience.

Back to our example: if you keep the test movie window open this time and select simulate download, you will see a completely different preloading animation which is a line of five white circles that show and hide. So you might be wondering, what in the heck happened to my loading animation? As discussed in the previous paragraph, it is still there, but the preloading mechanism which we use to load the TLF RSL is taking precedence. It goes first, and it runs until your entire SWF content has loaded, which means that your preloading animation runs, but in a similar situation to how it runs in test movie mode without simulate download selected, which means it is dismissed instantaneously.

How to Use the Custom Preloader Loop Feature

You can take back control of your preloading by editing the preloader method in the Advanced ActionScript 3.0 Settings dialog. Raise this dialog by clicking on the Edit… button for ActionScript Settings in the Properties panel. If you do not see this button, click on the stage to select nothing so that the Document properties are displayed in the Properties panel. Once this dialog is raised, there are two important settings you need to change:

  • In the Library path tab, change the Preloader method from Preloader SWF to Custom preloader loop.
  • Near the top, change Export classes frame near the top from 1 to 2.

Your dialog should look like this when you are done:

Advanced ActionScript 3.0 Settings dialog

Now it is time to add some ActionScript to frame one of the Actions layer. I’ll walk through the details later, but for now the code in that frame will look like this, so you can just copy and replace the whole block:

import fl.events.RSLEvent;

stop();

var swfComplete:Boolean = false;
var rslComplete:Boolean = false;

this.loaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoading);
this.loaderInfo.addEventListener(Event.COMPLETE, onComplete);
this.addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

function onLoading(evt:ProgressEvent):void {
	var loaded:Number = evt.bytesLoaded / evt.bytesTotal; 
	percent_txt.text = (loaded*100).toFixed(0) + "%";
};

function onComplete(event:Event):void { 
	this.loaderInfo.removeEventListener(ProgressEvent.PROGRESS, onLoading);
	this.loaderInfo.removeEventListener(Event.COMPLETE, onComplete);
	swfComplete = true;
	if (rslComplete && swfComplete) {
		gotoAndStop(2); 
	}
};

function onRSLComplete(e:RSLEvent):void
{
	this.removeEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);
	rslComplete = true;
	if (rslComplete && swfComplete) {
		gotoAndStop(2); 
	}
}

Now try test movie and simulate download again, and you will find your loading animation working and restored. One thing you might notice when you simulate download is that you get a white screen for much longer than you did without TLF. This is because when you specify the export classes frame as frame two, what this really means is export all classes that are not required on frame one in frame two. There is quite a bit of ActionScript code that still needs to be on frame one, and you see the blank, white screen while you wait for this to download.

On a tangent, SWF size can be a concern with TLF. Even if the image I added to the FLA earlier, my published SWF is showing up as 59.4KB in the SWF History section of the Properties panel when I use TLF with a custom preloader loop. The good news is this is a bit smaller than the 61.2KB I get if I use the default Preloader SWF preloader settings, but the bad news is that it is much bigger than the 9.4KB when I use classic text. But on the other hand if I change the Default linkage setting in the Advanced ActionScript 3.0 Settings dialog to Merged into code, my SWF size balloons up to 187.4KB! So the RSL approach for TLF is still winning you quite a bit, and because it is deployed as a signed, cacheable RSL, or a SWZ file, by default, each user should only have to download the RSL once for every SWF that they ever run that uses it, which means with luck your SWF will not be the one that takes the download hit for the RSL.

How does it work?

Preloader Method: Custom Preloader Loop

So let’s talk about what you’ve done with the changes you’ve made. First I’ll explain the preloader method change to custom preloader loop. The preloader SWF method exports your SWF basically unchanged, and then wraps that SWF in another automatically generated SWF. We take advantage of the DefineBinaryData swf tag and the Loader.loadBytes API to make this work. However, when you select custom preloader loop, instead we inject the RSL preloader code directly into your SWF. While this has many benefits, the reason we cannot do this by default is that since you retain complete control over the timeline, you are required to take all the additional steps.

Export Classes Frame

The first additional step required is the change to the export classes frame. While in this example we changed it to frame two, note that in general it needs to be changed to the first frame after your preloading animation is complete. In other words, you cannot play the frame specified in the export classes frame until you have verified that all RSLs are done loading.

It is educational to see what happens if you leave the export classes frame set to one. So try going back into the Advanced ActionScript 3.0 Settings dialog and switch the 2 back to a 1 and test movie. The first thing you will notice is some errors in the output panel:

VerifyError: Error #1014: Class flashx.textLayout.container::ContainerController could not be found.

ReferenceError: Error #1065: Variable ComponentShim is not defined.

ReferenceError: Error #1065: Variable Font_5 is not defined.

ReferenceError: Error #1065: Variable MainTimeline is not defined.

If you go to the compiler errors panel, you will see a warning that actually explains your situation:

Warning: 5010: Use of a custom preloader loop with the RSL for TLF Text requires that the export classes frame be set after the end of the preloader loop to avoid VerifyErrors.

Essentially what has happened is we have forced some classes into frame two that depend on the classes defined within the RSL. If you attempt to load these classes by playing frame two before the RSL is finished loading, then bad things happen.

On a side note, while it is not really recommended to put any TLF on stage in your preloader animation, believe it or not bad things do not happen if you put some TLF text on frame one. Well not especially bad, anyways. Try this: restore your export classes frame back to 2, create a new layer on your timeline put some TLF text in frame one that new layer, so that it should show up on both frame one and two at runtime. If you test movie, you will not get any errors and your text will show up correctly. However, if you try a simulate download to see what it looks like, you should not see the TLF text display at all on frame one, during the preloading animation, but it will appear on frame two. We have smarts behind the scenes that fails as gracefully as possible and makes the right thing happen.

ActionScript

Finally, let’s dig into the ActionScript itself. Let’s look at what has been added to the original preloader code:

import fl.events.RSLEvent;

This is simply an import for the RSLEvent type. Many classes are imported for you automatically when writing ActionScript on frames in Flash Professional, but not the RSLEvent class.

var swfComplete:Boolean = false;
var rslComplete:Boolean = false;

Your script could be simpler when you were only waiting for one event to happen before proceding to frame two, but now that you need to wait for two events, you must use Boolean variables to track each of them separately.

this.addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

The RSL_LOAD_COMPLETE event is a new event that is dispatched by the main timeline when all RSLs have finished downloading. In this case the only RSL is for TLF, but you would still only need to listen for this one event no matter how many RSLs were downloading.

	swfComplete = true;
	if (rslComplete && swfComplete) {
		gotoAndStop(2); 
	}

This code was added to the onComplete function. It marks swfComplete = true to indicate that the swf loading is finished, and then checks to ensure that rsl loading is complete before going to frame two.

function onRSLComplete(e:RSLEvent):void
{
	this.removeEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);
	rslComplete = true;
	if (rslComplete && swfComplete) {
		gotoAndStop(2); 
	}
}

This is the event listener function for the RSL_LOAD_COMPLETE event. The code is very similar to the code in the onComplete method, except for it sets the rslComplete Boolean to true.

Really pretty simple code. Not just one or two lines, but not too much more than is already required to make a preloader loop like this work.