How to Tell When an External SWF has Fully Loaded
It’s easy to load an external SWF or image at runtime. The MovieClip.loadMovie() method is all you need. Simply reference a given movie clip as your container — even if that’s _root or this — and supply the path to an external SWF or image as the method parameter. Just one line of code.
this.loadMovie("path/to/external/file.swf");
But how do you know when that file has finished loading? You may, for example, wish to provide a caption beneath a loaded JPG, or you may wish to center a loaded SWF, which would require knowing its MovieClip._width property — information that isn’t available until loading is complete.
An answer, short and sweet
Create an empty “container” movie clip and position it where you like on the Stage. Give it the instance name container via the Property inspector. This clip will hold the external file. If you haven’t already, create a new layer and name it “scripts” (or something else that lets you see at a glance where your scripts are). Click into a keyframe of your scripts layer — same keyframe as your container clip — and paste the following ActionScript.
container.loadMovie("yourFileHere.swf");
trace("Loading started!");
this.onEnterFrame = function() {
if (
container.getBytesLoaded() > 0 &&
container.getBytesLoaded() >= container.getBytesTotal()
) {
trace("Loading complete!");
trace("File width: " + container._width);
delete this.onEnterFrame;
} else {
trace("File width: " + container._width);
}
};
As you’ll see, the MovieClip._width property of the external file shows as zero until loading is complete.
How it works
The MovieClip class provides the two methods used to help us achieve our goal: MovieClip.getBytesLoaded() and MovieClip.getBytesTotal(). The first method returns the value of the external file’s currently loaded bytes; the second returns the value of the file’s total bytes (the total it “weighs”). Here, we compare these two values repeatedly until the former is greater than or equal to the latter. At that point, the external file has loaded.
To make the comparison repeatedly, we’re using the MovieClip.onEnterFrame event, which fires at approximately the speed of the movie’s frame rate (the default 12fps causes this event to fire twelve times a second). When loaded bytes are greater than or equal to total bytes, a trace() is performed — or whatever else you like — and the MovieClip.onEnterFrame loop is disposed of.
Now, there’s an additional expression in the condition of the if statement: this.getBytesTotal() >= 0 — what’s that for? Mainly, this is a failsafe for the following slight possibility. At the very beginning of a load, the value of getBytesTotal() might actually be zero, because the actual value of the file’s weight may not yet be known. In such a case, getBytesLoaded() and getBytesTotal() would be the same, even though no loading has begun! The logical AND operator, &&, ensures that both conditions are met.
Another answer, short and sweet
Different approach. Keep that empty “container” movie clip, same instance name, but erase your existing ActionScript and paste the following.
var mcl:MovieClipLoader = new MovieClipLoader();
mcl.loadClip("yourFileHere.swf", container);
trace("Loading started!");
trace("File width is: " + container._width);
var mclListener:Object = new Object();
mclListener.onLoadInit = function(mc:MovieClip) {
trace("Loading complete!");
trace("File width is: " + mc._width);
};
mcl.addListener(mclListener);
Here, too, the MovieClip._width property of the external file shows as zero until loading is complete.
How it works
This time, we’re using the MovieClipLoader class, new as of Flash MX 2004 (aka Flash 7). The difference here is that no repetitive value checking is needed, though SWFs using this code must be published for Flash Player 7 or higher.
A MovieClipLoader instance, here referenced as the variable mcl, invokes the MovieClipLoader.loadClip() method with the external file as one parameter and our container clip as the other. Next, a generic Object instance, here referenced as the variable mclListener, defines a function literal for the MovieClipLoader.onLoadInit event. Finally, the mcl instance “subscribes” mclListener as a listener. The event is raised after loading completes, which executes the function assigned to the event.
A reference to mcl’s target movie clip (container, in this case) is provided by the event’s return value. In this example, we reference that target with the mc parameter in the function literal.
Note: When I first encountered this class, I expected to use the MovieClipLoader.onLoadComplete event, but try it and you’ll see that the MovieClip._width property — indeed, any property — continues to show incorrectly at that point. See the ActionScript 2.0 Language Reference entry for details; specifically, the “MovieClipLoader class” entry’s Method summary.
Additional Reading
See “Loading and Tracking Multiple Files at the Same Time” to test for completion of multiple files.
April 17th, 2006 at 11:19 am
I like to use the following as opposed to:
If for that slip second, they are both equal to zero, the above will give
NaN>=1which is false, and when the condition is met normally, e.g.1024/1024>=1, then you can have the rest of the load code.Also, just curious why everyone uses
>=for comparing bytes loaded to the total - mainly, is it possible for thegetBytesLoaded()to be GREATER than thegetBytesTotal()?April 17th, 2006 at 11:22 am
Also, because _width is 0 until loading completes, you can use that to see when the clip loads:
April 17th, 2006 at 12:02 pm
NSurveyor,
You know, I really have no idea why so many people use
>=instead of simply==. From a logical standpoint, it shouldn’t ever be possible to have more bytes loaded than total, but>=certainly covers the bases, just in case.I like your idea of checking for
_width(or, heck, anyMovieClipproperty) to determine if a SWF has loaded. That’s a clever approach!December 1st, 2006 at 9:11 pm
script works great! but question tho, once the clip has loaded how does one stop it from replaying itself over and over each time i move my mouse over another button or link source? There must be a simple code to stop that from happening.
December 2nd, 2006 at 9:21 am
mike,
I’m not sure why a clip would replay just because you mouse over something else. A loaded SWF will play, certainly (unless it has a frame in its own timeline, or the one you’re loading it into, telling it to stop). You would specifically have to tell this loaded clip to restart due to an event handled for some other clip or button. Is that what you’re doing?
December 5th, 2006 at 2:00 pm
im using the “flipbook” script and loading a slideshow.swf into a page content. Everything works great, but when i mouse over the pages’ lower or right hand corner to flip it, the slideshow repeats itself. I tried the (stop) function but no use. Is there a way to load this movie and somehow make it stop from doing this? Any help you can give me would be highly appreciated. Thank you
December 5th, 2006 at 4:23 pm
mike,
Ah, a flipbook SWF is a horse of a different color!
I’ve seen a number of these online, so I don’t know which particular one you’re using — but in any case, complex script-based SWFs often don’t rely on timelines, so a
stop()action may have no practical effect. If your SWF references objects in the_root, who knows what might be happening (the_rootit thinks it’s referring to is now the_rootof the parent SWF, as discussed here).You might experiment with the
MovieClip._lockrootproperty, which overrides what a loaded SWF thinks is the root timeline.December 5th, 2006 at 6:12 pm
Im using the Flipbook 2.2 script. here is the site where im having the problems with http://www.xtrememediadesigns.com/flashsite (under portfolio section)
I will study more on Movie_Clip._lockroot that might be the answer.
Thank you
December 5th, 2006 at 6:38 pm
Mike,
Ah, PageFlip v2.2. Yes, that’s a good one. (Though I’ve only every played with it, not studied it.) I’d say
MovieClip._lockrootis a good place to start, but remember, all that does is override the normal behavior that occurs in loaded SWFs that reference_root, which PageFlip may or may not do.July 26th, 2007 at 2:09 pm
I load my swf in levels using loadMovieNum(). Then I check if a swf has been loaded using
_levelXXX.getBytesLoaded() == _levelXXX.getBytesTotal() && _levelXXX.getBytesTotal() > 0
However there seems to be a problem with it:
I am using the netlimiter program to set the download speed of my browser. If I set it to something low (5kB/sec) the movie never loads. The bytesloaded are stuck to a value lower than the bytestotal. Did anyone else have the same problem?
July 30th, 2007 at 7:14 pm
mike,
To find out what numbers your SWF is actually seeing, I recommend you use something like
trace()to “see under the hood.” That way you’ll know ifgetBytesLoaded()is never getting up togetBytesTotal()— and if so, by how much, so you can potentially compensate — or if it may even be going over. (Seems like that shouldn’t be possible, but stranger things have been known to happen.) Since you’re checking for perfect equality (==), there’s only one exact set of values that will work. If you use something like=< (_levelXXX.getBytesLoaded() - 5), just for example … that may be all the wiggle room you need.August 24th, 2007 at 4:10 pm
I have a problem not exactly the same as this topic, but maybe kind of related. I need to tell when the loaded swf is finished playing. I couldn’t find a way to make it work by loading the swf into a Movie Clip, but I did get it to work by using loadMovieNum –
onEnterFrame = function () {
if (_level1._framesloaded != undefined && _level1._currentframe == _level1._totalframes) {
control.Text = “Press continue to move on.”;
}
};
My problem now is that I can’t get my control buttons to go in front of the loaded movie. I tried swapDepths to no avail. Any ideas on either making the loadMovie method work, or moving the depth of my control bar movie clip above the loaded movie?
August 26th, 2007 at 7:53 pm
Denise,
Assuming the loaded content is linear — that is, it has visually or conceptually reached its end when its timeline has run its course — then yes, a comparison of the
MovieClip._currentframeandMovieClip._totalframesproperties is a great way to determine when the loaded content has finished playing. The key in that scenario is the comparison, as you’ve shown, and actually has nothing to do with the mechanism used to load the content (could beloadMovie(),loadMovieNum(),MovieClip.loadMovie(), theMovieClipLoaderclass, or anything else that loads a SWF into a timeline). TheloadMovieNum()function is a somewhat dated approach, and it loads content into a level rather than a movie clip symbol, but even so … levels are just stacked documents played by Flash Player: in the end, even they are simply movie clips, just like the main timeline is a movie clip. (Actually, they’re just other main timelines._level0is the default main timeline, and_level1has its own main timeline, and so on.)The reason you can’t get your control buttons to appear over your other content is almost certainly because your buttons are in
_level0, whose content can never appear in levels that are sequentially higher than it. TheMovieClip.swapDepths()method only works with movie clips that share the same parent timeline — and these don’t. Consider usingMovieClip.loadMovie()— or better, theMovieClipLoaderclass — and use the same_currentframeand_totalframesproperties on whatever timeline (movie clip) contains the loaded content. By keeping your loaded content in the same level as your buttons, you’ll be able to useswapDepths()on them, assuming they both appear in the same timeline (e.g., your buttons and the container are both in the main timeline, or both nested in the same movie clip). Or you could simply sidestep the need to swap by ensuring that your buttons appear in a higher layer of the main timeline than the container clip into which your loaded content appears.August 28th, 2007 at 12:19 pm
David,
Thanks for your response — that explains why swapDepths doesn’t work! I’m still not getting this to work however. It seems the key is comparing current to total frames on the loaded movie, NOT the loader mc. I’ve tried the following 3 methods:
loadMovie: When I compare the _currentframe & _totalframes properties on the movie clip that contains the loaded content, it’s finding a match from frame 1 because it’s referencing those properties on the loader movie clip which only has 1 frame.
loadMovieNum: Here I was able to reference the timeline of the loaded movie by the level number — this worked great other than the problem with the buttons being behind the movie.
MovieClipLoader: Used on the name given to the loaded movie, I get this error “There is no property with the name ‘_currentframe’.” Once again if I reference the loader mc, I get a match at frame 1.
In case you’re wondering, I’m trying to get this to work because I’m loading multiple existing swfs created by “Captivate” and need to flag the end of each movie with an fscommand in order to enable the user to go on to the next movie.
August 28th, 2007 at 12:51 pm
Denise,
That depends on the nature of the loaded content. I haven’t experimented with Captivate personally, but my hunch is that it outputs SWFs in a non-linear fashion. (Don’t quote me on this … just a hunch.) If Captivate SWFs are nested at all — and I think this is likely — you may indeed have to dig a level deeper, which is leading you in the direction you’re thinking.
In actual practice, the loader movie clip, in a sense, becomes the loaded content.
The first line creates my loader clip (though this could have been an empty movie clip symbol dragged by hand to the stage and given an instance name.
The middle portion instantiates
MovieClipLoaderand anObjectinstance and prepares the way for aMovieClipLoader.onLoadInitevent handler. As you can see, we’ll be tracing theMovieClip_currentframeand_totalframesproperties of the container clip, referenced by its themcvariable (could just as easily be its instance name,loader).The final line loads the external SWF into the loader clip. The output I get is this:
2 of 40
3 of 40
4 of 40
5 of 40
6 of 40
7 of 40
8 of 40
9 of 40
10 of 40
11 of 40
// etc.
… which, interestingly, skips the first frame — but nonetheless picks up, as expected, in recounting the frames of the loaded content (not the loader clip).
In your situation, the actual multi-framed content must be nestled in a container clip within the Captivate SWF’s main timeline. Even if you’re able to drill down to something that contains a timeline of some length, I’m not entirely sure the timeline you find will necessarily represent the full extent of the SWF’s animation.
August 29th, 2007 at 12:10 pm
It works! I didn’t know to use the MovieClipLoader.onLoadInit event handler. Thanks a million!
August 29th, 2007 at 12:22 pm
Denise,
Glad to hear it!
Yeah, there are a handful of ways to handle events in ActionScript 2.0. AS3 simplifies that (in most cases) to a single approach. In this case,
onLoadCompletemay work just as well; it’s just thatonLoadInitnot only waits for the external SWF to load, it also waits for any code (if available) in the first frame of the loaded SWF to execute.October 13th, 2007 at 1:51 pm
Any idea what would cause MovieClipLoader.loadClip to return false? I’ve checked security settings, moved this call out of a constructor. I’m calling this from an AS object that’s instantiated from the Actions panel of an FLVPlayback component. Does this need to be called from a clip that’s explicitly placed on the stage somewhere?
For the life of me, I can’t figure this one out…
Thanks!
October 13th, 2007 at 7:06 pm
Rosieman,
I sounds like you’ve probably checked the documentation already, but for sake of completeness,
loadClip()returns “trueif the URL request was sent successfully; otherwisefalse.” So theoretically something about your request is going batty, and security settings would probably be among the first things to come to my mind, too — outside of double- and triple-checking that the requested file is located where it should be — and you’ve already checked security settings.I’ve used
MovieClipLoaderinstances in plenty of constructors without issue, so its placement in your constructor function shouldn’t be a problem. Not 100% sure what you mean by “instantiated from the Actions panel of an FLVPlayback component,” though. Are you attaching code directly to your component? That shouldn’t be a problem, although I encourage you to give your component an instance name and keep all your ActionScript in keyframes.As long as your
MovieClipLoaderinstance is valid, it really shouldn’t matter where you end up referencing it from. I personally tend to keep most of my code in a single layer of the main timeline (or in external class files) … something about your mention of “called from a clip” makes me think you’re attaching most of your code directly to objects, but maybe you’re talking about keyframe code inside a nested clip. Either which way, none of it should matter, and I’m afraid nothing leaps out at me immediately as an error on your part, given your description so far.