Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning ActionScript 2.0 2006

.pdf
Скачиваний:
101
Добавлен:
17.08.2013
Размер:
12.47 Mб
Скачать

Chapter 7

Library content can be placed on the stage by assigning a linkage ID and then using the attachMovie() method.

External content can be loaded by using the movie clip loadMovie() method, or by using the global loadMovieNum() function. Using loadMovie() is the preferred way to load external content.

Movie clips can be used as masks, which allow some underlying content to show through and other content to remain hidden.

Some strategies for improving playback performance include using bitmap caching; minimizing the use of transparency, filter, and blending mode effects; hiding content using the _visible property; and refraining from making projects full screen.

Exercises

1.Create a one-week calendar using createEmptyMovieClip(), createNewTextField(), and the movie clip drawing methods to draw it out. The calendar should consist of seven movie clips, each containing a text field and each outlined with a square. The calendar should also have next week and previous week buttons that call functions to go forward and back by one week, plus there should be a label for the current month. Consult with the Date object in the Flash help panel for the date methods to help you out. Hint: var todaysDate:Date = new Date(); gets today’s date.

2.Create two movie clips on the main timeline. Draw a square in the first clip and a triangle in the second clip by using the movie clip drawing methods. Apply the second movie clip as a mask over the first clip, and then switch them and apply the first movie clip as a mask over the second movie clip.

3.Extend exercise 2 to make the triangle clip move to follow the mouse. The code for responding to mouse movement is as follows:

var mouseListener:Object = new Object(); mouseListener.onMouseMove = function ()

{

// Code to respond to mouse motion goes here

};

Mouse.addListener(mouseListener);

198

8

Pre-Loading Movies

When loading external content, situations frequently occur in which you need to wait until the content has completely loaded before you can work with that content. You also may have some external content that is quite large, and you just want to provide some feedback on the load progress.

Pre-loaders are useful any time content needs to be loaded, such as when the main movie is initially loaded, when movies are requested on demand, when images are loaded, or even when large amounts of XML data need to be transferred from a server. A pre-loader ensures that the content has completely loaded before allowing it to run. Once the content starts to download, it is periodically polled to see how many bytes have been loaded. When the number of bytes loaded is the same as the total number of bytes, the movie can proceed with the next step.

This chapter looks at a number of different pre-loading approaches. You go into the guts of creating one yourself so that you can see how a pre-loader actually works, explore the MovieClipLoader class, and learn to use the Loader and ProgressBar components. Finally you examine pre-loaders in the context of two project architectures. The first is the monolithic movie approach where all content is contained in a single .swf file. The second is the split-up movie approach, where a smaller shell is initially loaded, and subsequent content is loaded on demand.

Using a Custom Pre-Loader

The main tools you need to create a pre-loader from scratch are the getBytesLoaded() and getBytesTotal() methods, plus a timing mechanism. Here’s what a first stab at it might look like:

Each of these code snippets is available in the downloadable content for this book. Feel free to experiment with each snippet.

var intervalID:Number;

this.createEmptyMovieClip(“myMovie”, this.getNextHighestDepth()); myMovie.loadMovie(“http://www.nathanderksen.com/book/trailer.swf”);

Chapter 8

intervalID = setInterval(monitorProgress, 100);

function monitorProgress() : Void

{

var percentDone:Number;

percentDone = myMovie.getBytesLoaded() / myMovie.getBytesTotal() * 100; trace(“percentDone: “ + percentDone);

if (myMovie.getBytesTotal() == myMovie.getBytesLoaded())

{

clearInterval(intervalID); trace(“Content has finished loading.”);

}

}

This code creates a new movie clip to hold the content, starts the load process, and polls every 100 milliseconds to see how far the download has gotten. The timing mechanism takes the form of the setInterval() function, which is designed to call a designated function at predefined intervals, independent of whatever else is going on. The monitorProgress() function calculates the percent complete, and tests to see whether the movie has finished loading.

This is a good start, but the trace output that is generated when the code is run shows the following:

percentDone: NaN

Content has finished loading.

The problem is that it takes a little while for the player to contact the server to find out the size of the file. In the meantime, it returns zero, so when the percent done value is calculated, it suffers from a divide- by-zero error. (The value NaN that is returned stands for Not a Number, and commonly indicates a divide-by-zero error.) Adding a check for a zero value fixes this:

var intervalID:Number;

this.createEmptyMovieClip(“myMovie”, this.getNextHighestDepth()); myMovie.loadMovie(“http://www.nathanderksen.com/book/trailer.swf”); intervalID = setInterval(monitorProgress, 100);

function monitorProgress() : Void

{

var percentDone:Number;

if (myMovie.getBytesLoaded() > 0)

{

percentDone = myMovie.getBytesLoaded() / myMovie.getBytesTotal() * 100; trace(“percentDone: “ + percentDone);

if (myMovie.getBytesTotal() == myMovie.getBytesLoaded())

{

clearInterval(intervalID); trace(“Content has finished loading.”);

}

}

}

200

Pre-Loading Movies

That’s definitely better. Now, nothing is done until it is known how large the loaded content is. Unfortunately, the movie starts playing as soon as the first bit of content loads, and you want to delay when the movie starts playing. One solution is to stop the playhead and hide the clip as soon as the movie starts to load. When the movie has finished loading, playback is allowed to start and the clip is displayed:

var intervalID:Number;

this.createEmptyMovieClip(“myMovie”, this.getNextHighestDepth()); myMovie.loadMovie(“http://www.nathanderksen.com/book/trailer.swf”); intervalID = setInterval(monitorProgress, 100);

function monitorProgress() : Void

{

var percentDone:Number; myMovie.stop();

myMovie._visible = false;

if (myMovie.getBytesLoaded() > 0)

{

percentDone = myMovie.getBytesLoaded() / myMovie.getBytesTotal() * 100; trace(“percentDone: “ + percentDone);

if (myMovie.getBytesTotal() == myMovie.getBytesLoaded())

{

clearInterval(intervalID);

myMovie.play(); myMovie._visible = true;

trace(“Content has finished loading.”);

}

}

}

The only drawback is that there’s no way for the person viewing the content to know the progress of the content loading. Create a function that draws out a progress bar and call it from the monitorProgress() function:

var intervalID:Number;

this.createEmptyMovieClip(“myMovie”, this.getNextHighestDepth()); this.createEmptyMovieClip(“progressMovie”, this.getNextHighestDepth()); progressMovie._x = 60;

progressMovie._y = 120; progressMovie.createTextField(“percentDoneLabel”, ;

progressMovie.getNextHighestDepth(), 40, 0, 100, 20);

myMovie.loadMovie(“http://www.nathanderksen.com/book/trailer.swf”); intervalID = setInterval(monitorProgress, 100);

function monitorProgress() : Void

{

var percentDone:Number; myMovie.stop(); myMovie._visible = false;

if (myMovie.getBytesLoaded() > 0)

201

Chapter 8

{

percentDone = myMovie.getBytesLoaded() / myMovie.getBytesTotal() * 100; showProgress(percentDone);

trace(“percentDone: “ + percentDone);

if (myMovie.getBytesTotal() == myMovie.getBytesLoaded())

{

clearInterval(intervalID);

myMovie.play(); myMovie._visible = true; progressMovie._visible = false;

trace(“Content has finished loading.”);

}

}

}

function showProgress(percentDone:Number) : Void

{

var barWidth:Number = percentDone;

progressMovie.percentDoneLabel.text = String(Math.ceil(percentDone)) + “ %”; progressMovie.clear();

//Draw a border progressMovie.moveTo(0, 20); progressMovie.lineStyle(1, 0x666666); progressMovie.lineTo(100, 20); progressMovie.lineTo(100, 30); progressMovie.lineTo(0, 30); progressMovie.lineTo(0, 20);

//Draw the bar progressMovie.moveTo(0, 20); progressMovie.beginFill(0xCCCCCC, 100); progressMovie.lineTo(barWidth, 20); progressMovie.lineTo(barWidth, 30); progressMovie.lineTo(0, 30); progressMovie.lineTo(0, 20); progressMovie.endFill();

}

First, a movie clip is created to hold the loading animation. Every time checkProgress() is called, the showProgress() function is also called. When loading is complete, the pre-loader animation is hidden.

You now have a pre-loader that provides control over the loaded content and provides feedback to the user, yet the implementation could be cleaned up a bit. The hitch is that this code is very specific to the project, with movie clip references embedded right in the checkProgress() function. It would help if you could pass a couple of parameters instead of hard-coding the references, like this:

var intervalID:Number;

this.createEmptyMovieClip(“myMovie”, this.getNextHighestDepth()); this.createEmptyMovieClip(“progressMovie”, this.getNextHighestDepth()); progressMovie._x = 60;

progressMovie._y = 120;

202

Pre-Loading Movies

progressMovie.createTextField(“percentDoneLabel”, progressMovie.getNextHighestDepth(), 40, 0, 100, 20);

myMovie.loadMovie(“http://www.nathanderksen.com/book/trailer.swf”); intervalID = setInterval(monitorProgress, 100, progressMovie, showProgress);

function monitorProgress(progressBarHolder:MovieClip, ;

progressUpdater:Function) : Void

{

var percentDone:Number; myMovie.stop(); myMovie._visible = false;

if (myMovie.getBytesLoaded() > 0)

{

percentDone = myMovie.getBytesLoaded() / myMovie.getBytesTotal() * 100; progressUpdater(percentDone, progressBarHolder);

trace(“percentDone: “ + percentDone);

if (myMovie.getBytesTotal() == myMovie.getBytesLoaded())

{

clearInterval(intervalID);

myMovie.play(); myMovie._visible = true;

progressBarHolder._visible = false; trace(“Content has finished loading.”);

}

}

}

function showProgress(percentDone:Number, progressBarHolder:MovieClip) : Void

{

var barWidth:Number = percentDone;

progressBarHolder._visible = true;

progressBarHolder.percentDoneLabel.text = String(Math.ceil(percentDone))+” %”; progressBarHolder.clear();

//Draw a border progressBarHolder.moveTo(0, 20); progressBarHolder.lineStyle(1, 0x666666); progressBarHolder.lineTo(100, 20); progressBarHolder.lineTo(100, 30); progressBarHolder.lineTo(0, 30); progressBarHolder.lineTo(0, 20);

//Draw the bar

progressBarHolder.moveTo(0, 20); progressBarHolder.beginFill(0xCCCCCC, 100); progressBarHolder.lineTo(barWidth, 20); progressBarHolder.lineTo(barWidth, 30); progressBarHolder.lineTo(0, 30); progressBarHolder.lineTo(0, 20); progressBarHolder.endFill();

}

This code is now easier to re-use because the hard-coded references to the progress bar holder and to showProgress() have been changed to parameters that can be passed in instead.

203

Chapter 8

Polling with onEnterFrame() Versus setInter val()

There is a variation of the preceding technique to poll for the number of bytes loaded. setInterval() was last used to call a function at predetermined intervals, but there’s another timing mechanism that can be used as well: the onEnterFrame() event handler. Every time the playhead advances to another frame, an onEnterFrame() event handler is called by the player. You can make use of this to call your polling code. Here’s an example of how:

var intervalID:Number;

this.createEmptyMovieClip(“myMovie”, this.getNextHighestDepth()); this.createEmptyMovieClip(“progressMovie”, this.getNextHighestDepth()); progressMovie._x = 60;

progressMovie._y = 120; progressMovie.createTextField(“percentDoneLabel”, progressMovie.getNextHighestDepth(), 40, 0, 100, 20);

myMovie.loadMovie(“http://www.nathanderksen.com/book/trailer.swf”); this.onEnterFrame = function()

{

monitorProgress(this, progressMovie, showProgress);

}

function monitorProgress(contentHolder:MovieClip, progressBarHolder:MovieClip, ; progressUpdater:Function) : Void

{

var percentDone:Number; myMovie.stop(); myMovie._visible = false;

if (myMovie.getBytesLoaded() > 0)

{

percentDone = myMovie.getBytesLoaded() / myMovie.getBytesTotal() * 100; progressUpdater(percentDone, progressBarHolder);

trace(“percentDone: “ + percentDone);

if (myMovie.getBytesTotal() == myMovie.getBytesLoaded())

{

clearInterval(intervalID);

myMovie.play(); myMovie._visible = true;

progressBarHolder._visible = false; delete contentHolder.onEnterFrame; trace(“Content has finished loading.”);

}

}

}

function showProgress(percentDone:Number, progressBarHolder:MovieClip) : Void

{

var barWidth:Number = percentDone;

progressBarHolder._visible = true;

204

Pre-Loading Movies

progressBarHolder.percentDoneLabel.text = String(Math.ceil(percentDone))+” %”; progressBarHolder.clear();

//Draw a border progressBarHolder.moveTo(0, 20); progressBarHolder.lineStyle(1, 0x666666); progressBarHolder.lineTo(100, 20); progressBarHolder.lineTo(100, 30); progressBarHolder.lineTo(0, 30); progressBarHolder.lineTo(0, 20);

//Draw the bar progressBarHolder.moveTo(0, 20); progressBarHolder.beginFill(0xCCCCCC, 100); progressBarHolder.lineTo(barWidth, 20); progressBarHolder.lineTo(barWidth, 30); progressBarHolder.lineTo(0, 30); progressBarHolder.lineTo(0, 20); progressBarHolder.endFill();

}

In this code, the onEnterFrame() event from the base timeline is captured and used. You could have captured the event from the myMovie clip; however, the act of loading content into that movie clip removes the handle to your custom onEnterFrame() event handler after the first time through. An additional parameter is sent through to the monitorProgress() function so that when the clip has finished loading, the custom event handler can be removed.

The reason why one technique might be used over another is largely personal. Some find onEnterFrame() easier to work with, and are accustomed to using it for animation. Others prefer the setInterval() technique because it is kinder on the main processor for slower machines and allows flexibility with how frequently the progress bar is to be updated. Although we generally prefer the setInterval() technique, you should use whichever one is more comfortable for you.

Understanding the MovieClipLoader Class

Now that you have created a full-fledged pre-loader, take a look at another option, the MovieClipLoader class. The advantage of this class is that it takes care of a lot of these details for you and it gives fine control over what code gets called at which stage in the load process. Following are the events and methods that are available with the MovieClipLoader class.

MovieClipLoader Class Events

The MovieClipLoader class provides a number of events that fire at different points through the process of loading a movie clip or image. These events allow you to customize how your code interacts with externally loaded media throughout the loading process. You see examples of these events in use along with the introduction of the MovieClipLoader class methods. The following table describes the events of the MovieClipLoader class:

205

Chapter 8

Event

Type

Description

 

 

 

onLoadComplete

Listener

Fires when the all the content has been downloaded.

onLoadError

Listener

Fires when the content load process fails.

onLoadInit

Listener

Fires when the first frame of the loaded content has been

 

 

run. For images, this happens after onLoadComplete.

 

 

For movie clips, this happens before onLoadComplete.

onLoadProgress

Listener

Fires every time content is written to disc.

onLoadStart

Listener

Fires when the content has successfully begun to

 

 

download.

MovieClipLoader Class Methods

Four class methods are available for you to use to load and unload media, to get the load progress, and to work with events. The following table delineates the methods of the MovieClipLoader class:

Method

Return

Description

 

 

 

addListener

Boolean

Binds the loader instance to an object that defines the

 

 

event handlers.

getProgress

Object

Provides access to the bytes loaded and total bytes for

 

 

the currently loading movie clip.

loadClip

Boolean

Starts the loading of a movie clip. Used instead of the

 

 

movie clip loadMovie() method.

unloadClip

Boolean

Removes the movie clip that was loaded with

 

 

loadClip().

The following sections demonstrate how to use these methods.

addListener()

The addListener()method binds the loader to an object holding the event handlers. It takes a single parameter, which is a handle to an object holding the event handler code. The following prototype shows the basic form of the addListener() method:

loaderObject.addListener(listener:Object) : Boolean

Here’s an example implementation that loads an external movie clip called section1.swf into a movie clip called sectionHolder. The example demonstrates how to create an event listener, and to link the listener with the loader. By creating a custom method with the listener object that has the same name as the name of the event to be handled, the method is automatically called when the event takes place.

this.createEmptyMovieClip(“sectionHolder”, this.getNextHighestDepth());

var loadListener:Object = new Object();

206

Pre-Loading Movies

loadListener.onLoadComplete = function(contentHolder:MovieClip)

{

trace(“Content has been loaded”);

};

var contentLoader:MovieClipLoader = new MovieClipLoader(); contentLoader.addListener(loadListener); contentLoader.loadClip(“section1.swf”, sectionHolder);

getProgress()

The getProgress()method is used to manually get the progress of the loaded content. Normally, the onLoadProgress event provides this information.

This method takes a single parameter, which is a handle to the movie clip container. It is a way of explicitly getting the bytes loaded and bytes total values for the movie clip being loaded. The return object contains two parameters, bytesLoaded and bytesTotal. The following prototype shows the basic form of the getProgress() method:

loaderObject.getProgress(targetClip:MovieClip) : Object

The method returns an anonymous object with two properties, bytesLoaded and bytesTotal. Here’s an example:

var contentLoader:MovieClipLoader = new MovieClipLoader();

this.createEmptyMovieClip(“sectionHolder”, this.getNextHighestDepth());

function checkProgress()

{

var statusObject:Object = contentLoader.getProgress(contentHolder);

trace(“bytes loaded: “ + statusObject.bytesLoaded); trace(“bytes total: “ + statusObject.bytesTotal);

}

setInterval(checkProgress, 50); contentLoader.loadClip(“section1.swf”, sectionHolder);

loadClip()

The loadClip()method triggers the actual loading of the media and is used instead of the movie clip loadMovie() method. The MovieClip.loadMovie() method also works for loading external media, however it does not allow you to work with load events as easily as the MovieClipLoader class does.

The loadClip() method takes two parameters: the URL for the movie clip to load and a handle to the movie clip container that is to receive the content. The following prototype shows the basic form of the loadClip() method:

loaderObject.loadClip(url:String, target:Object) : Boolean

207