Hey everyone,
in the last chapter we made it to load a SWF and unload it on demand. Let's continue here and imagine that we're having three SWFs, each of them having some content which should be shown on click. A typical usage would obviously be a Flash Website consisting of multiple SWFs.
What we're going to do:
At the end of this article we're having three SWFs loaded which can be displayed seperately on click. Sounds pretty trivial but takes some management as well. If you are just interested in that "loading and displaying" click here to scroll to the bottom.
Why is here so much text?
Much of this article doesn't really cover this single purpose but is more going into the general question of how to handle multiple loading-processes. I have seen many Flash projects with a cruel loading management which definetly leads into a "I don't like Flash" attitude. Knowing how to load data is something the HTML world doesn't really have to take care for but we do have and we should think about how to use this option. We can give the user the feeling of getting much content without waiting, we just have to check when it's time to load this.
Thoughts about storing data or always un- & reload it:
My experiences showed me that loading Flash data only on click is one of the most reasons people don't like flash. Why not using the time the user spent on the website to prepare the rest of the content? In 9 of 10 projects that left my table the customer wanted it like everyone's liking it: We're loading data and store it in the cache - just like any video streaming does and not "Why is there again and again a Preloader?" Of course this strongly depends on the project. It doesn't make sense to cache videos or a large amount of images if this would lower the overall performance. Leads me to the general question:
When to load content?
At the very first you have to decide whether to load every Data at once and after all loading processes are done start showing the content or loading just the first files, showing content pretty fast and then start loading the rest in the background while the user can already read content.
Loading the entire content is useful when dealing with a complete size of 2-3MB. The user has to wait in the beginning but then can be sure everything is loaded. I've to admit that I don't use this way too often, the reason is simple: You can do so in one SWF then as well and it makes the whole page work better together. Anyway, the good thing about using multiple SWFs is that you can easily update content without compiling the whole project again and besides working in a team on that is much easier.
Let's check the other option: Loading the first SWF (or the small-sized first, nevermind), adding it to stage and loading the rest in the background. This way can be pretty useful when having slightly bandwith-using content, e.g. a gallery. Does the user really have to wait for the gallery when he opens the site? He might be waiting for 20mb but the site is 1mb without the gallery. So loading it after the rest is absolutely necessary - but wouldn't it be annoying as well if the gallery would start loading only on click? You've then got to wait for it to completely load but you spent already some time on the website - so why not use this time to load the gallery in the background? I'd say this is a big advantage of Flash to load content the user will see. Imagine following: The user opens your website, waiting some seconds for the first content. Then he's spending 30 seconds on it - enough time for the gallery to complete loading. He clicks on gallery and there it is, on click and he didn't even had to wait.
Conclusion to that:
In most cases a mix of both is the right way. You're going to load all the small content elements, such as news, contact, whatever - and load the heavy data like portfolios, galleries, videos, ... at last. The user will not be disappointed when he's seing the gallery-preloader at 60% when clicking it at first but he will continue surfing on the rest of the page till the gallery is loaded. The best case would be that the gallery is completely loaded when the user clicks of course. You've got to find the way in between performance lowering because of caching too much and serving content on demand. Keep in mind that doing this kind of load-management requires some coding knowledge as you have to work with multiple loaders called by different times. But hang on, this will be the #3 on this article series.
Now finally back to the topic: Let's load some SWFs and display it on click.
Load multiple SWFs and store them
As said I'm going to cover the complexity of having different loading processes managed in the next chapter, so at first we're going to load every SWF in the beginning, storing them and show them when wanted. Let's declare some variables first.
-
var _swfPathArr:Array = new Array("00.swf", "01.swf", "02.swf");
-
var _loadedSWFs:int;
-
-
var _swfClipsArr:Array = new Array();
-
var _swfTempClip:MovieClip;
Pretty self-explaining I guess. The temporary MovieClip will be used to quickly store the data (mainly for reasons of adding additional things to it while defining. You'll see in a minute) Now we'll call the function which will start the whole process.
-
startLoading(_swfPathArr);
-
-
function startLoading(pathArr:Array):void {
-
_swfLoader = new Loader();
-
_swfRequest = new URLRequest();
-
-
loadSWF(pathArr[0]);
-
}
-
-
function loadSWF(path:String):void {
-
setupListeners(_swfLoader.contentLoaderInfo);
-
-
_swfRequest.url = path;
-
_swfLoader.load(_swfRequest);
-
}
-
-
function setupListeners(dispatcher:IEventDispatcher):void {
-
dispatcher.addEventListener(Event.COMPLETE, onSwfComplete);
-
dispatcher.addEventListener(ProgressEvent.PROGRESS, currentSwfProgress);
-
}
-
-
function currentSwfProgress(event:ProgressEvent):void {
-
var _perc:int = (event.bytesLoaded / event.bytesTotal) * 100;
-
// swfPreloader.percentTF.text = _perc + "%";
-
}
Using just one Loader instance for all files:
You might have noticed yet that we've got only one Loader instance yet to load each SWF after another. This is because it's making much more sense in this case than loading all files at the same time with multiple loaders. You've got much more control about every loading process, it's easier to determine errors and the speed remains the same. Besides it's much easier to code.
But nevertheless there are cases in which you definetly should work with many instances, e.g. a gallery with many images. I'm going to get into that deeper in the next article as well.
Do the loop and storing
The loader shall continue loading the SWFs while the completely loaded are stored - until every SWF is stored and then the first SWF can be added to stage. The onSwfComplete function is called the first time after - you might know - the first SWF is completely loaded :)
-
function onSwfComplete(event:Event):void {
-
event.target.removeEventListener(Event.COMPLETE, onSwfComplete);
-
event.target.removeEventListener(ProgressEvent.PROGRESS, currentSwfProgress);
-
-
_swfTempClip = event.target.content;
-
_swfTempClip.customID = _loadedSWF;
-
-
_swfClipsArr.push(_swfTempClip);
-
-
if(_loadedSWFs <_swfPathArr.length - 1) {
-
_loadedSWFs++;
-
loadSWF(_swfPathArr[_loadedSWFs]);
-
} else {
-
onCompletePreloading();
-
}
-
}
We're removing the EventListener at first. Just to make sure, you know. Then we're setting the _swfTempClip to the SWF we've just successfully loaded and add an ID to it. Only to check if everything works fine later on. We're pushing it into our Array to store it - the temp clip will be overwritten the next time, but the already loaded one was pushed into our Array. Then the loop is triggered. If the count (which started at 0) is below the count of SWFs we wanna load, we're going to start the loading of the next swf. We're passing the new path as _count is now 1, therefore the second path is used. When every SWF is loaded the onPreloadComplete(); function is called.
Let's imagine we've got three buttons to show the different SWFs. We know for sure, that the first file we've declared in our _swfPathArray has got the Array-Index 0 in our _swfClipsArr. So we can easily access it with _swfClipsArr[0]. To make this really usable for the Flash IDE (don't throw stones on me loved FDT users :)) we're going to easily switch the button's name to load the correct SWF. Besides we've got a MovieClip called "contentContainer" on the stage (yea, the visual stage with a named MC in the Properties!) which holds the SWF.
-
function onCompletePreloading():void {
-
contentContainer.addChild(_swfClipsArr[0]);
-
-
news_btn.addEventListener(MouseEvent.CLICK, setContent);
-
portfolio_btn.addEventListener(MouseEvent.CLICK, setContent);
-
contact_btn.addEventListener(MouseEvent.CLICK, setContent);
-
}
-
-
function setContent(event:MouseEvent):void {
-
var _swfToAdd:MovieClip;
-
-
switch(event.target.name) {
-
case "news_btn":
-
_swfToAdd = _swfClipsArr[0];
-
break;
-
-
case "portfolio_btn":
-
_swfToAdd = _swfClipsArr[1];
-
break;
-
-
case "contact_btn":
-
_swfToAdd = _swfClipsArr[2];
-
break;
-
}
-
-
contentContainer.removeChildAt(contentContainer.numChildren-1);
-
contentContainer.addChild(_swfToAdd);
-
trace(_swfToAdd.customID);
-
}
It's simple as that. We're removing the old SWF from the stage (by determing it to be the last Object added to the MC) and add the new one. The _swfClipsArr's index is equal to the _swfPathArr entries. The trace at the end shows that we're still having control of custom properties we gave our loaded SWFs.
The result:
You can easily test it yourself. Create three SWFs and one loading-SWF. Create three MCs (for the buttons) and an empty MC (as contentContainer) on the loading-SWF's stage. Name the MCs properly and copy the following snippet to your first frame. If you didn't know yet: Testing to display loaded files (from 1MB upwards) in Flash locally cause a lag while adding it to stage. But testing online gives the wanted result. The SWF is changed directly on click.
Here it is:
-
var _swfLoader:Loader;
-
var _swfRequest:URLRequest;
-
-
var _swfPathArr:Array = new Array("00.swf", "01.swf", "02.swf");
-
-
var _swfClipsArr:Array = new Array();
-
var _swfTempClip:MovieClip;
-
var _loadedSWFs:int;
-
-
-
startLoading(_swfPathArr);
-
-
function startLoading(pathArr:Array):void {
-
_swfLoader = new Loader();
-
_swfRequest = new URLRequest();
-
-
loadSWF(pathArr[0]);
-
}
-
-
function loadSWF(path:String):void {
-
setupListeners(_swfLoader.contentLoaderInfo);
-
-
_swfRequest.url = path;
-
_swfLoader.load(_swfRequest);
-
}
-
-
function setupListeners(dispatcher:IEventDispatcher):void {
-
dispatcher.addEventListener(Event.COMPLETE, onSwfComplete);
-
dispatcher.addEventListener(ProgressEvent.PROGRESS, currentSwfProgress);
-
}
-
-
function currentSwfProgress(event:ProgressEvent):void {
-
var _perc:int = (event.bytesLoaded / event.bytesTotal) * 100;
-
// swfPreloader.percentTF.text = _perc + "%";
-
}
-
-
-
function onSwfComplete(event:Event):void {
-
event.target.removeEventListener(Event.COMPLETE, onSwfComplete);
-
event.target.removeEventListener(ProgressEvent.PROGRESS, currentSwfProgress);
-
-
_swfTempClip = event.target.content;
-
_swfTempClip.customID = _loadedSWFs;
-
_swfClipsArr.push(_swfTempClip);
-
-
if(_loadedSWFs <_swfPathArr.length - 1) {
-
_loadedSWFs++;
-
loadSWF(_swfPathArr[_loadedSWFs]);
-
} else {
-
_swfLoader.unloadAndStop();
-
_swfLoader = null;
-
onCompletePreloading();
-
}
-
}
-
-
function onCompletePreloading():void {
-
contentContainer.addChild(_swfClipsArr[0]);
-
-
news_btn.addEventListener(MouseEvent.CLICK, setContent);
-
portfolio_btn.addEventListener(MouseEvent.CLICK, setContent);
-
contact_btn.addEventListener(MouseEvent.CLICK, setContent);
-
}
-
-
function setContent(event:MouseEvent):void {
-
var _swfToAdd:MovieClip;
-
-
switch(event.target.name) {
-
case "news_btn":
-
_swfToAdd = _swfClipsArr[0];
-
break;
-
-
case "portfolio_btn":
-
_swfToAdd = _swfClipsArr[1];
-
break;
-
-
case "contact_btn":
-
_swfToAdd = _swfClipsArr[2];
-
break;
-
}
-
-
contentContainer.removeChildAt(contentContainer.numChildren-1);
-
contentContainer.addChild(_swfToAdd);
-
trace(_swfToAdd.customID);
-
}
Please feel free to correct me and notice, that this snippet is simplified, e.g. IO_ERRORs must be listened, fallbacks must be defined, etc. I just wanted to keep the focus on loading, storing and adding multiple SWFs.



February 16th, 2010
Marvin Blase
Posted in
Tags:
Hi Marvin,
thank you so much for this article! Made me see some things clearer now and because I am going to work with loading stuff in future more often I will definetly follow your tips and think about everything before starting. Thanks again, also for your time to write such a detailed & rich article.
Kind regards,
Raphael.
THANKS FOR THE HEADS UP MARVIN! Just checked the other post and read your quick note, and so I went to the root page on this site. wow this article is amazing. going to read over it ALL carefully. THANK YOU SIR for taking the time and writing such thorough explanations. you should have a PAYPAL DONATE button by the article. IM SERIOUS!
THank you,
AS3 Student.
Would donate as well! :-D Seriously this was a lot of work which really helped me taking a deeper look at this topic. Many many thanks to you, Marvin!
thanks so much. i was searching so long for a solution to load multiple swfs from an array. this works like a charm!
hey, you're all welcome :) glad to help! stay tuned for part three which again will get deeper into loading processes, e.g. preloading images from an xml.
[...] ≠ Musiker & Grafiker # Die beste Idee wird als Open Source Projekt umgesetzt! # The finer Art of Loading (#2) - It's all about timing! # Auf MovieClips & Funktionen von externen SWFs [...]
I tried the code and got an error 2035 message. I apparently have not properly specified the path to the .swf/s (to-be-loaded). All my .swf's are in the same directory.
I look forward to understanding ALL this code and to using it. Thank you for help.
Wicked tutorial! You are a genius! With clean code. You've made a lot of people incredibly happy.
Heads up: If by the luck of the draw the child swf file that's being loaded into the parent swf happens to include some actionscript that consist of building components programmatically rather than dragging and dropping from the Components window, you might run into trouble. For example, in one of my child swfs, I'm programmatically loading a DataGrid object; in a second one, I'm programmatically creating a Scrollbar. In my case, I'm getting "TypeError: Error #2007: Parameter child must be non-null" errors.
I haven't quite pinpointed the culprit yet. You're more than welcome to provide a tip. Keep it real!
hey I am loving what I think this does
and can't wait until #3
but was wondering if a fla could at all be provided.
(I know it seems lazy but I am very visual and things click so quick
if I have a fla accompaniment)
I am currently playing with it and It is not working the way I know it should which I am certain is because I am missing something (read the article 4x ;-) )
either way thanks
hey everyone, sorry for letting you wait for some days, haven't been able to response here.. but now :)
@Patrick Hill: well, this one's might occur because of the missing button-instances on stage. i will upload an example-fla tonight which might help you then!
@Michael Roebuck: thanks - i'm not quite sure why this happens, but i guess the error depends on a timing problem. the swfs try to execute the code in their docclass/first frames though not completely initiated. try to use the INIT-event instead of COMPLETE. maybe send me your files so i can have a closer look at them..
@sConvey: hey, inasmuch? i mean, what's wrong? :) without any information i can't do too much but my first guess is "are the button-instances on your stage declared with names?" - because the lines 59-61 are trying to add eventlistener to movieclips which are already on stage (with these names in properties-window set). keep on asking, i'm always glad to help :)
Marvin thanks for the response.
I have 3 buttons on my stage, each appropriately instantiated.
I have also created a MC instantiated as contentContainer
in the same directory as my .fla I have three swf's named 00, 01, 02.swf
when I test the swf's independently using flash player they play correctly.
however when I attempt to load them into the .fla using this code I get many
TypeError: Error #1009: Cannot access a property or method of a null object reference.
errors.
I am using in each of the swf's a component called slideshowpro/ and thumbgrid
(as the content I want to load are the galleries on my site) and their names are often referenced in the errors.
I do have all of the image data, xml files and everything else needed to play those swfs in the
same directory...
any ideas?
I am sure I am dong something silly. I am quite new to loading external files and also still relatively green to coding in general.
thanks for your help... cheers
Fantastic tutorial, thanks. As with most people here, I'm trying to grasp AS3, this has been very helpful. I was able to get everything working, however, when you select a button to load the .swf, how do you do a crossover fade? I would like to have a smooth transition from 00.swf to 01.swf etc. I'm able to get the swf to fade in, but only after you remove the previous clip. Is there a way to remove the clip once the next one has loaded.
Thanks... Colin
Could you post the code without all the line numbers? We can't copy and paste with all that in there. Thanks!
just use the plain text button at the top left corner.
I'm new to flash and have been trying to figure out how to load a series of swf files, one after the other without any button. That is after swfFile01 completes, swfFile02 begins, and after it completes, swfFile03, etc.
Thanks.
can this be used for a gallery where the thumbnails are "synched", ie. when the first thumbnail is loaded it begins to load next and so forth. i tried to "hack" it in order to do so but my limited AS3 skills doesn't let me.. :-)
right now I load about 20 thumbnails all at once, it slows the whole thing down, plus it doesn't look that good when it's added to the stage randomly.
hey johannes,
for sure - this it what it's all about :) just use the last snippet loads multiple swfs sequentially, means one after another. in the end you've got an array holding all your loaded clips, accessible with clipsArr[0] for the first one, e.g.
i'm going to publish part #3 soon, especially going deeper into building a gallery that way.
This is EXACTLY what I needed after three days of blood, sweat and tears! I am absolutely over the moon to finally get the coding side of my website sorted. Thank you SO much for sharing this.
This is a great tutorial that really breaks down how to properly preload multiple swfs. this can easily be applied to images. Thanks!
Another tool I've used on multiple occasions to preload all swfs or images at once is team2p0's implementation, that also returns an array of files you wish to preload
http://www.microhome.com.co/com/team2p0/Preload.as
simply awesome! thanks
Thanks for this excellent read. I liked every little bit of it. I bookmarked this and will be reading again.
Very nice thank you but as a previous poster wrote this snippet of code will not load my xml scroller (Error #1009: Cannot access a property or method of a null object reference.) that's contained in my external swf. The snippet from your chapter 1 will load the scroller though. hmmmm....
Where & how do you want to access it?
I get this message.
ReferenceError: Error #1056: Cannot create property customID on _1_fla.MainTimeline__Preloader__.
at MainTimeline_fla::MainTimeline/onSwfComplete()
Here is my code:
var _swfLoader:Loader;
var _swfRequest:URLRequest;
var _swfPathArr:Array = new Array("_1.swf", "_2.swf", "_3.swf", "_4.swf", "_5.swf", "_6.swf");
var _swfClipsArr:Array = new Array();
var _swfTempClip:MovieClip;
var _loadedSWFs:int;
startLoading(_swfPathArr);
function startLoading(pathArr:Array):void {
_swfLoader = new Loader();
_swfRequest = new URLRequest();
loadSWF(pathArr[0]);
}
function loadSWF(path:String):void {
setupListeners(_swfLoader.contentLoaderInfo);
_swfRequest.url = path;
_swfLoader.load(_swfRequest);
}
function setupListeners(dispatcher:IEventDispatcher):void {
dispatcher.addEventListener(Event.COMPLETE, onSwfComplete);
dispatcher.addEventListener(ProgressEvent.PROGRESS, currentSwfProgress);
}
function currentSwfProgress(event:ProgressEvent):void {
var _perc:int = (event.bytesLoaded / event.bytesTotal) * 100;
// swfPreloader.percentTF.text = _perc + "%";
}
function onSwfComplete(event:Event):void {
event.target.removeEventListener(Event.COMPLETE, onSwfComplete);
event.target.removeEventListener(ProgressEvent.PROGRESS, currentSwfProgress);
_swfTempClip = event.target.content;
_swfTempClip.customID = _loadedSWFs;
_swfClipsArr.push(_swfTempClip);
if(_loadedSWFs <_swfPathArr.length - 1) {
_loadedSWFs++;
loadSWF(_swfPathArr[_loadedSWFs]);
} else {
_swfLoader.unloadAndStop();
_swfLoader = null;
onCompletePreloading();
}
}
function onCompletePreloading():void {
contentContainer.addChild(_swfClipsArr[0]);
_1.addEventListener(MouseEvent.CLICK, setContent);
_2.addEventListener(MouseEvent.CLICK, setContent);
_3.addEventListener(MouseEvent.CLICK, setContent);
_4.addEventListener(MouseEvent.CLICK, setContent);
_5.addEventListener(MouseEvent.CLICK, setContent);
_6.addEventListener(MouseEvent.CLICK, setContent);
}
function setContent(event:MouseEvent):void {
var _swfToAdd:MovieClip;
switch(event.target.name) {
case "_1":
_swfToAdd = _swfClipsArr[0];
break;
case "_2":
_swfToAdd = _swfClipsArr[1];
break;
case "_3":
_swfToAdd = _swfClipsArr[2];
break;
case "_4":
_swfToAdd = _swfClipsArr[2];
break;
case "_5":
_swfToAdd = _swfClipsArr[2];
break;
case "_6":
_swfToAdd = _swfClipsArr[2];
break;
}
contentContainer.removeChildAt(contentContainer.numChildren-1);
contentContainer.addChild(_swfToAdd);
trace(_swfToAdd.customID);
}
Any Ideas?
Also... when are we gonna see that 3rd tutorial?
Thanks a bunch
Hey Hallabak,
what happens if you cast _swfTempClip as a MovieClip before assigning the customID-property? Like _swfTempClip = MovieClip(event.target.content);
A third tutorial is still in progress but will come soon - thanks for reminding me :)
Hey guys, I'm running into a problem. I'm receiving these errors:
1061: Call to a possibly undefined method unloadAndStop through a reference with static type flash.display:Loader.
...as well as: 1120: Access of undefined property contentContainer.
Hey Patrick,
a) you have to use Flash Player 10
b) you have to set up a clip named contentContainer to the stage.
Marvin,
Thanks for your reply. I didn't realize I had to publish it in Flash Player 10, which is tricky with CS3...but I found a way. Yes, I dropped the contentContainer on the stage, but put it on the wrong place (I had to put it inside the movieClip where my button was). All is well now. I can't begin to tell you how much this post and site have helped me. Thank you so very much.
You're welcome :)
Hey Marvin, I'm back. :( So, I thought I had this thing licked, but now I'm getting another error...the same one that Hallaback is getting. I didn't get this error until upgrading to CS5 two days ago and voila, I'm getting ReferenceError: Error #1056: Cannot create property customID on test1_fla.MainTimeline__Preloader__.at test/onSwfComplete().
I've read quite a bit about the built in preloader in CS5 and tried everything I could find on the internet (mainly messing with the AS3 advanced publish setting) to no avail. I also tried your suggestion to Hallback, but it didn't work. Can you think of anything else?
My code:
var _swfLoader:Loader;
var _swfRequest:URLRequest;
var _swfPathArr:Array = new Array("test1.swf", "test2.swf", "test3.swf");
var _swfClipsArr:Array = new Array();
var _swfTempClip:MovieClip;
var _loadedSWFs:int;
startLoading(_swfPathArr);
function startLoading(pathArr:Array):void {
_swfLoader = new Loader();
_swfRequest = new URLRequest();
loadSWF(pathArr[0]);
}
function loadSWF(path:String):void {
setupListeners(_swfLoader.contentLoaderInfo);
_swfRequest.url = path;
_swfLoader.load(_swfRequest);
}
function setupListeners(dispatcher:IEventDispatcher):void {
dispatcher.addEventListener(Event.COMPLETE, onSwfComplete);
dispatcher.addEventListener(ProgressEvent.PROGRESS, currentSwfProgress);
}
function currentSwfProgress(event:ProgressEvent):void {
var _perc:int = (event.bytesLoaded / event.bytesTotal) * 100;
// swfPreloader.percentTF.text = _perc + "%";
}
function onSwfComplete(event:Event):void {
event.target.removeEventListener(Event.COMPLETE, onSwfComplete);
event.target.removeEventListener(ProgressEvent.PROGRESS, currentSwfProgress);
_swfTempClip = event.target.content;
_swfTempClip.customID = _loadedSWFs;
_swfClipsArr.push(_swfTempClip);
if(_loadedSWFs <_swfPathArr.length - 1) {
_loadedSWFs++;
loadSWF(_swfPathArr[_loadedSWFs]);
} else {
_swfLoader.unloadAndStop();
_swfLoader = null;
onCompletePreloading();
}
}
function onCompletePreloading():void {
contentContainer.addChild(_swfClipsArr[0]);
news_btn.addEventListener(MouseEvent.CLICK, setContent);
portfolio_btn.addEventListener(MouseEvent.CLICK, setContent);
contact_btn.addEventListener(MouseEvent.CLICK, setContent);
}
function setContent(event:MouseEvent):void {
var _swfToAdd:MovieClip;
switch(event.target.name) {
case "news_btn":
_swfToAdd = _swfClipsArr[0];
break;
case "portfolio_btn":
_swfToAdd = _swfClipsArr[1];
break;
case "contact_btn":
_swfToAdd = _swfClipsArr[2];
break;
}
contentContainer.removeChildAt(contentContainer.numChildren-1);
contentContainer.addChild(_swfToAdd);
trace(_swfToAdd.customID);
}
HELP!!
Im a graphic designer aka a copy and past actionscripter. LOL!~
Im getting this error... Error #1034: Type Coercion failed: cannot convert flash.display::AVM1Movie@28eeadc1 to flash.display.MovieClip.