How to Tell When an External SWF has Fully Loaded

Flash ActionScript 2.0

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.

19 Responses to “How to Tell When an External SWF has Fully Loaded”

  1. NSurveyor Says:

    I like to use the following as opposed to:

    
    container.getBytesLoaded() > 0 &&
    container.getBytesLoaded() >= container.getBytesTotal()
    
    container.getBytesLoaded()/container.getBytesTotal()>=1

    If for that slip second, they are both equal to zero, the above will give NaN>=1 which 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 the getBytesLoaded() to be GREATER than the getBytesTotal()?

  2. NSurveyor Says:

    Also, because _width is 0 until loading completes, you can use that to see when the clip loads:

    
    container.loadMovie("yourFileHere.swf");
    trace("Loading started!");
    this.onEnterFrame = function() {
      if (container._width>0) {
        trace("Loading complete!");
        trace("File width: " + container._width);
        delete this.onEnterFrame;
      } else {
        trace("File width: " + container._width);
      }
    };
    
  3. David Stiller Says:

    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, any MovieClip property) to determine if a SWF has loaded. That’s a clever approach!

  4. mike Says:

    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.

  5. David Stiller Says:

    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?

  6. mike Says:

    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

  7. David Stiller Says:

    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 _root it thinks it’s referring to is now the _root of the parent SWF, as discussed here).

    You might experiment with the MovieClip._lockroot property, which overrides what a loaded SWF thinks is the root timeline.

  8. Mike Says:

    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

  9. David Stiller Says:

    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._lockroot is 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.

  10. mike stiv Says:

    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?

  11. David Stiller Says:

    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 if getBytesLoaded() is never getting up to getBytesTotal() — 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.

  12. Denise Myers Says:

    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?

  13. David Stiller Says:

    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._currentframe and MovieClip._totalframes properties 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 be loadMovie(), loadMovieNum(), MovieClip.loadMovie(), the MovieClipLoader class, or anything else that loads a SWF into a timeline). The loadMovieNum() 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. _level0 is the default main timeline, and _level1 has 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. The MovieClip.swapDepths() method only works with movie clips that share the same parent timeline — and these don’t. Consider using MovieClip.loadMovie() — or better, the MovieClipLoader class — and use the same _currentframe and _totalframes properties 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 use swapDepths() 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.

  14. Denise Myers Says:

    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.

  15. David Stiller Says:

    Denise,

    It seems the key is comparing current to total frames on the loaded movie, NOT the loader mc.

    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.

    var mc:MovieClip = this.createEmptyMovieClip("loader", 0);
    
    var mcl:MovieClipLoader = new MovieClipLoader();
    var listener:Object = new Object();
    listener.onLoadInit = function():Void {
      mc.onEnterFrame = function():Void {
        trace(mc._currentframe + " of " + mc._totalframes);
      }
    }
    mcl.addListener(listener);
    
    mcl.loadClip("loadMe.swf", mc);

    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 MovieClipLoader and an Object instance and prepares the way for a MovieClipLoader.onLoadInit event handler. As you can see, we’ll be tracing the MovieClip_currentframe and _totalframes properties of the container clip, referenced by its the mc variable (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.

  16. Denise Myers Says:

    It works! I didn’t know to use the MovieClipLoader.onLoadInit event handler. Thanks a million!

  17. David Stiller Says:

    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, onLoadComplete may work just as well; it’s just that onLoadInit not 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.

  18. Rosieman Says:

    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!

  19. David Stiller Says:

    Rosieman,

    I sounds like you’ve probably checked the documentation already, but for sake of completeness, loadClip() returns “true if the URL request was sent successfully; otherwise false.” 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 MovieClipLoader instances 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.

    Does this need to be called from a clip that’s explicitly placed on the stage somewhere?

    As long as your MovieClipLoader instance 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.

Leave a Reply