Flash Actionscript 3 (AS3) Super Loader – taterboy
May 17th, 2008 | Filed under: Flash AS2 to AS3

I really enjoy coding in AS3, but one of the things I dislike the most is the fact that when building movieclips as Classes, you have to export to Action Script or place all your movieclips on the stage somewhere. I think the latter is not very flexible. I understand why it has to be done; I just don’t like it. The problem with exporting everything to the Action Script is that it loads that MovieClip on the first frame, and loaders that are normally placed on the first frame will not show up or function until the first frame is loaded. This first frame now contains all our content that we have exported to Action Script which really defeats the purpose of a loader. That is why you will see a blank white box for a few seconds or longer before the loader shows up. The best way to fix this is to build a loader swf  to load our current project swf into. 

But you know what would be even cooler?  A Super Loader!
This super loader could be so smart, that we could code it once, then use it as a loader for every project we do in the future. It will load in any-size swf at any frame-rate, and to get even sicker, let’s make the loader bar or display animation external so that we can make many different loaders  for different projects and have them work with this super loader as well. 

So welcome to Super Loader and Super Stage.
A few of the features we will be using to pull this off are the new Stage frameRate, Loader and ProgressEvent classes.

Step 1: Host SWF and SuperStage Class.
The host file is a blank fla that points to our SuperStage Class. We do not care about what size it is, what color the background is or the frame rate. We will be able to control all that later with our SuperStage Class.

The Code:
Open the package and import all the needed Classes. I am organizing these classes more like I would if I were working on a large project. Hence the com.HDI.stage. You could remove this and place the .as file in the same directory as the .fla if that is easier.
 

package com.HDI.stage {
	/*import flash.display.Sprite;
	import flash.display.Stage;
	import flash.display.StageDisplayState;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;*/
	import flash.display.Stage;
	import flash.display.StageDisplayState;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.net.URLRequest;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.display.MovieClip;
	import com.HDI.events.loaderEvent;
 
	public class SuperStage extends MovieClip {
		//External Variables:
		//var thisWidth:Number = 800;
		//var thisHeight:Number = 600;
		var fps:Number = 30;
		var swfURL:String = "imgViewer.swf";
		var ldrURL:String = "loaderbar.swf";
 
		//Loader Variables:
		var loadComplete:Boolean = false;
		var loadPerc:Number;
		var loaderCnt:Number = 0;
 
		//Internal Objects:
		var mainMovie:Object;
		var mainLdr:Object;
		var mainContainer:MovieClip;
		var loaderMC:MovieClip;
 
		public function SuperStage():void{
			init();
		}
		private function init():void{
			//Set all the stage properties onload
			stage.showDefaultContextMenu = false;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
 
			//load the main container and the loader container
			/*
			this is not manditory and can make things more complex,
			but so that the loader always stays on top, I add these to MovieClips
			then load everything else inside these clips.
			Load the loaderMC second so that it remains on top.
			*/
			mainContainer = new MovieClip();
			addChild(mainContainer);
			loaderMC = new MovieClip();
			addChild(loaderMC);
 
			//setup stage init incase I need it later.
			loaderInfo.addEventListener(Event.INIT,initStage);
 
			//start to load the loader bar.
			loadObject(loaderMC,ldrURL,null,ldrFin);
			stop();
			addEventListener(loaderEvent.LOAD,loadNewObject);
 
		}
		function initStage(ev:Event):void{
			//stage.stageWidth = thisWidth;
			//stage.stageHeight = thisHeight;
		}
	}
}

 
It is supposed to be a best practice to not have a lot of code in your class method. I use an init function to set everything up. This is a very similar practice to flex programming when you call an init function on creationComplete. Another advantage is that we can call init(), if it is public, from an external source or to reload/reset the class.

In this init function we will set up all of our stage properties. When everything loads we want to have a loader bar that appears in the center of the screen while the main content is loading. Being a super loader, it will need to appear on top of everything, and we can use it later to load in other content. When Flash loads new content, it will load new stuff on top of the old stuff. So to make sure our loader stays on top without swapping depths all the time, we will add a main content MovieClip to the stage first, then add our loader MovieClip. Now load all the content into the main content clip which will always exist below the loader MovieClip.

Step 2: Main Loader Engine

Using the Object Loader method we can load in our loader bar, main content, and any other swfs or images in the future. 

 

function loadNewObject(ev:loaderEvent):void{
	loadObject(ev.ldrTarget,ev.ldrURL,mainLdr,ev.ldrFunction);
}
//main loader method
function loadObject(targ:Object,path:String,mon:Object,funFunction:Function):void{
	var ldr:Loader = new Loader();
	var ldrReq:URLRequest = new URLRequest(path);
	ldr.load(ldrReq);
	ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, funFunction);
	ldr.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, ldrProgress);
	ldr.name = "ldr" + loaderCnt;
	targ.addChild(ldr);
	loaderCnt ++;
}
//finish loading. first two items loaded will be ldr0(loader bar) and ldr1(main content)
function ldrFin(ev:Event):void{
	if(ev.target.loader.name == "ldr0"){
		mainLdr = ev.target.content;
		mainLdr.loadStat = true;
		mainLdr.x = (stage.stageWidth/2)-(mainLdr.width/2);
		mainLdr.y = (stage.stageHeight/2)-(mainLdr.height/2);
		loadObject(mainContainer,swfURL,mainLdr,ldrFin);
	}
	else if(ev.target.loader.name == "ldr1"){
		mainMovie = ev.target.content;
		stage.frameRate = ev.target.frameRate;
	}
}
//progress handler, sends loading info to loader bar.
function ldrProgress(ev:ProgressEvent):void{
	if(mainLdr){
		mainLdr.ldrMon(ev.bytesLoaded/ev.bytesTotal);
	}
}

Note:
The targeting of objects after they are loaded can be a little tricky. There is the main movie clip as the parent, the loader object, then the loader content. If you are managing a large amount of MovieClips then it may be a good idea to add each object to an array as they are loaded. 

The frameRate is set when the main content loads. The width and height  and background colors are established by the html host file which makes this loader pretty flexible. Right now you have to change the urls to the loader and swf, which isn’t so super, but they can be moved out into the html embed code as variables or we can change the names of our files to be more generic. My next post will go into more depth about implementing this into a workflow and how to automatically configuring the super loader on publish. So come back soon.

In the meantime, we can work on our loader bar.
You can see in the following code that there is a loader bar and monitoring code for the content being loaded. We also set the loaders x and y position to be dead center of our stage dynamically depending on the size of the main content.

package com.HDI.loaders {
 
	import flash.display.Sprite;
	import flash.display.MovieClip;
 
	public class LdrBar extends MovieClip{
		//load status. I may need this later
		public var loadStat:Boolean;
		public function LdrBar():void{
			stop();
			init();
		}
		public function init():void{
			//On init, loader animation and bar size is reset.
			//The best part of use the init function instead of the
			//class method, is that I can call this anytime to reset the loader.
			ani.stop();
			bar.scaleX = 0.01;
		}
		public function ldrMon(perc:Number):void{
			//main loader animation control.
			//this method is called from the host using the progress event.
			//as the progress changes, the loader becomes visible and the animation plays.
			if(perc < 1){
				visible = true;
				bar.scaleX = perc;
				ani.play();
			}
			else{
				// when progress stops, loader becomes invisible and animation stops.
				ani.stop();
				visible =false;
 
			}
		}
	}
}

Step 3: Loader Bar Animation and Image Viewer.
The Super part of this loader is the fact that you can create new loader  graphics, animation, and progress indicators; and load those in without changing any code in our super stage. The host file only cares about loading in something; it can even be an image. Then the loader will receive and update every time the progress changes. It would be a pretty boring loader if it were an image, but the point has been made that it could be.

This loader acts like a loader bar with an animation effect that is visible when progress is updated and invisible when there is no progress. I have included a simple image viewer to show how to use the same loader for everything that is loaded into our main movie. If you have a fast connection, then you may not see much and will have to take my word for it, It looks awesome! And how do we call the loader feature from our main movie? Using a dispatchEvent  and a custom event of course, you can read more about that here if you haven’t already.

I hope you enjoyed this tutorial and stay tuned for the next post which will take these super components and integrate them into a workflow that can only be described as, well, “Super”.

source

UPDATE:

Someone asked how to remove loaded items. The image viewer example above just stacks one image on top of another.  If you have swfs with music and/or animation this can cause problems. If you are using the same MovieClip as a holder for all of your loaded items, then place a new MovieClip as a child of the holder and load all items in the child, that way you can remove the childClip before loading new objects.

This is a modification that can beadded to the imgViewer.fla in the source files.

function mouseHandler(ev:MouseEvent):void{
	imgLoader.visible = false;
	instructions.visible = false;
	//-- new code
	if(imgLoader.numChildren > 0){
		imgLoader.removeChild(imgLoader.getChildByName("imgHolder1"));
	}
	var tempIMG:MovieClip = new MovieClip();
	tempIMG.name = "imgHolder1";
	imgLoader.addChild(tempIMG);
	// -- end new code
 
	dispatchEvent(new loaderEvent(loaderEvent.LOAD,tempIMG,"images/3d_sketch.jpg",imgLoaded));
	// replace all instances of "imgLoader" with "tempIMG" in the event dispatchers.
 
}

***Cleaner way is to use the childMonitor Class and the pullAll() method to remove all items before loading a new item.

import com.hdi.util.ChildMonitor;
var cmt:ChildMonitor = new ChildMonitor(imgLoader);
 
function mouseHandler(ev:MouseEvent):void{
	imgLoader.visible = false;
	instructions.visible = false;
	//-- new code
	if(cmt.childList.length > 1){
		cmt.pullAll();
	}
	// -- end new code
 
	dispatchEvent(new loaderEvent(loaderEvent.LOAD,imgLoader,"images/3d_sketch.jpg",imgLoaded));
 
}

Technorati Tags: , , , ,

Related Posts:

|

13 Comments »

  1. I am looking at this tutorial and am completely lost I am new at Flash and I developed a fantastic website but I have having the problem you described at the top with the white screen while it was loading. The problem is I do not really understand what exactly we are doing in this tutorial. Can you please help me out with this, I understand that I am probably sounding pretty stupid right now but I just can’t seem to follow what we are doing here and how to implement it with my website. I could really use dumb down one click at a time instructions or a download with instruction on implementation. I would really appreciate your help with this. Thank you

    Comment by Terrasa — August 5, 2008 @ 6:38 pm

  2. Let me correct my last comment I found the source link and was wondering if you could just explain how to use it. I am needing to load my flash website and have no idea how to adapt your source to suit my needs.

    Comment by Terrasa — August 5, 2008 @ 7:26 pm

  3. I hope I am not too late to help.

    The easiest way to use the loader files is to goto:
    http://www.taterboy.com/blog/2008/06/exporting-swf-files-with-custom-html-templates/
    1. Download the source.

    2. In the source folder there is an swf called “mainFlash.swf”. You can use it as a base and loader for your website. All you have to do is configure the “mainFlash.html” file.

    3. modify the following in the AC_FL_RunContent arguments.

    ‘width’, ‘414′,
    ‘height’, ‘208′,

    ‘FlashVars’,’swfURL=sample.swf’

    Update them to the correct height, width and file name of your Flash file.

    If you want to use your existing source files, then navigate to com/HDI/stage/SuperStage.as.
    1. Open the file and edit the following code.

    var swfURL:String = “imgViewer.swf”;
    Change the name to your Flash file name.

    2. Recompile the “mainFlash.swf”, then update the HTML file with the correct height and width.

    If you need to use the loader again to load in other swfs or images into your Flash file, you can use this line of code.
    dispatchEvent(new loaderEvent(loaderEvent.LOAD,imgLoader,”images/3d_sketch.jpg”,imgLoaded));

    Change “imgLoader” to that name of the MovieClip you want to load the new swf into,
    Change “images/3d_sketch.jpg” to the path of your swf or image file.
    Change “imgLoaded” to the name of the function you want the loader to call once loading of the new file is complete.

    Good luck, if you have any further questions let me know.

    Comment by taterboy — August 6, 2008 @ 6:31 am

  4. Cool thank you for your help. The loader is now working great and you directions were fantastic but for some reason the music on my site (which I put in using Action script) is not playing all other sound play fine though. Can you help me with this as well?

    Comment by Terrasa — August 6, 2008 @ 10:29 am

  5. Oh and am I able to use my index.html instead of the mainFlash.html? I am going to try it now but it would be good to hear from the source incase I mess up or something.

    Comment by Terrasa — August 6, 2008 @ 10:30 am

  6. I can help, do you have it up somewhere or do you want to post the code?

    Comment by taterboy — August 6, 2008 @ 10:36 am

  7. it is located at my website. Which if you click on my name you will get to.

    Comment by Terrasa — August 6, 2008 @ 10:39 am

  8. I am not seeing anything load now. I saw it a few minutes ago and heard audio.

    Comment by taterboy — August 6, 2008 @ 10:58 am

  9. Awesome work Tater, this is really great.

    No seriously, this made my day after reading through it. I have a feeling a lot of people will also find this helpful after they figure out it exists. Let the news spreading begin!

    Comment by JD — August 7, 2008 @ 7:33 pm

  10. oh. p.s.
    This is going in the resources section of our version cue, if you don’t mind. =D

    Comment by JD — August 7, 2008 @ 7:36 pm

  11. Thanks.

    Comment by taterboy — August 8, 2008 @ 5:14 am

  12. Hi, this is a great script, thanks!
    I have a .fla where I want to use this loader, how can I do it without using the mainFlash.swf?

    Comment by Lidia — August 23, 2008 @ 6:11 pm

  13. If you just want to use the loader and nothing else, then load the “loaderbar.swf” into your fla,

            var ldr:Loader = new Loader();
    	var ldrReq:URLRequest = new URLRequest("loaderbar.swf");
    	ldr.load(ldrReq);
    	ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, finHandler);
    	addChild(ldr);
     
    var mainLdr:Object;
    function finHandler(ev:Event):void{
    	mainLdr = ev.target.content;
    }

    Then for the progress events of other loading objects, add this code.

    function ldrProgress(ev:ProgressEvent):void{
    	if(mainLdr){
    		mainLdr.ldrMon(ev.bytesLoaded/ev.bytesTotal);
    	}
    }

    If you load anything else in the same parent as the loader bar, then you will need to load it so that the loader bar still appears on top of everything.

    Comment by taterboy — August 24, 2008 @ 3:22 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment