ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources
Dynamic Slide Show AS3 Complete Guide. Part 2: Loading Pictures
http://www.actionscript.org/resources/articles/904/1/Dynamic-Slide-Show-AS3-Complete-Guide-Part-2-Loading-Pictures/Page1.html
Jean André Mas
Graphic designer converted since 2004 to coding. I play around with C++, OpenGL, Java, Javascript, AND Actionscript.
My website: ASWC 
By Jean André Mas
Published on July 13, 2009
 

A Dynamic Slide Show is quite a simple application but the more dynamic you want it, the more complex it gets. This series of articles will show you how to create a dynamic, flexible, and versatile slide show with AS3. Each new article will be covering a particular subject in the creation of a Slide Show. The articles will be as follow:

1. Loading data (or why everybody uses XML?)
2. Loading Pictures (or How do I time this?)
3. Resizing and Auto resizing (or can the swf resizes itself?)
4. Adding Setup Options (or letting the user take charge)
5. Adding Effects and Create Custom Effects (or Put The BitmapData Class to Work)
6. Randomizing (or Adding Some Life)
7. Adding Texts (or The Advantage of Classes)
8. Adding Sound (or Turn That Off!)
9. Cleaning Up (or Learn How To Take The Trash Out)
10. CMS (or Live And Let Live)


Loading Pictures (or How do I time this?)

Now that we know how to load our data, we can know load also our pictures which is the next logic step in the process.  Let’s start a new Flash document and document class and apply the same setting than in the preceding section.

We start by importing a new class, the Loader class:

[as]import flash.display.Loader;[/as]

And of course declare a variable to hold a Loader object:

[as]private var picture_loader:Loader;[/as]

And in our completeHandler function we can instantiate it:

[as]picture_loader = new Loader();[/as]

We use a loader object to load assets like pictures or swfs so that’s exactly what we’ll do here. Let’s give it a try. We use our XML data to pass a path to our Loader and then add the Loader to the stage:

[as]picture_loader.load(new URLRequest(xml_pictures.PICTURES.PICTURE_PATH[0].toString())); addChild(picture_loader);[/as]

Perfect, we now see the first picture! PICTURE_PATH[0] points to the first picture path in our XML data, PICTURE_PATH[1] would point to the second one and so on. We can also use this: PICTURE_PATH.length() to know how many pictures we are dealing with. You can see how easy is to work and access data with XML. Now we need a way to loop through the data and load a picture at a determined interval.

HOW DO I TIME THIS?

There’s many ways to do this, we could use an ENTER_FRAME event which would tie the slide show to the frame rate, we could also use a Timer which would give us more freedom, it is not very time accurate but we could improve it by using the getTimer() method. I chose the Timer for this tutorial.

We’ll store in a variable how many pictures we have and create also a variable to hold the time each picture will stay displayed, we also create an index variable that we use to pick up a picture path from the xml data, then finally we create a Timer to trigger the loading of picture at a regular interval, we of course import the Timer class

[as]import flash.utils.Timer;//import the class private var number_of_picture:uint;//will hold how many pictures we have private var current_index:uint;//used to pick a picture path private var display_time:Number;//how long should a picture stay on the screen private var display_timer:Timer;//used to time the dsiplaying of pictures[/as]

Now we should set all these variables only once the XML data is available so we do this in the completeHandler function:

[as]number_of_picture = xml_pictures.PICTURES.PICTURE_PATH.length(); display_time = 5;//let's set it to 5 for now display_timer = new Timer(display_time*1000);//create our timer, 1000 is one second so display_time*1000 sets the timer to the right time[/as]

Now all we have to do is call a function to load our first picture and then let the timer call this same function at regular intervals to load the next pictures:

[as]display_timer.addEventListener(TimerEvent.TIMER, get_next_picture); display_timer.start(); get_next_picture();[/as]

Now let’s write our get_next_picture function:

[as]private function get_next_picture(e:TimerEvent = null):void{//set the event default value to null so we can call it without passing an event if(current_index == number_of_picture){current_index = 0;}//if we looped through all picture paths we go back to 0 (first picture) picture_loader = new Loader(); //create a new loader to hold our picture picture_loader.load(new URLRequest(xml_pictures.PICTURES.PICTURE_PATH[current_index].toString()));//load our picture using current_index addChild(picture_loader);//display the picture current_index++;//increase the current_index so next time we run the function it will load the next picture }[/as]

Perfect! It shows all pictures one after another and cycle through. But there’s a lot of things not working well here. We never remove the pictures that are not displayed anymore so they keep adding on top of each other. Our Timer is not very accurate, it triggers the function at more or less 5 seconds so we should make it more accurate, the timer triggers the loading of the picture and not the displaying of the picture so if we really want the picture to show after 5 seconds we should load it first and then display it when the time comes. Let’s address all these issues one by one in the next page.


Loading Pictures (or How do I time this?) part 2
First let’s remove the pictures we don’t need anymore, we could add this at the end of our get_next_picture function:

[as]while(numChildren>2){ removeChildAt(0); }[/as]

Only problem is if you were to add other objects to the slide show like textfields for example this code would break, so we need another approach. As always there’s many ways to go about this, here is what I chose to do. Instead of adding the pictures to the stage, I will add them to a main Sprite and then use the preceding code for the main Sprite instead. So let’s modify our code and add a main Sprite:

[as] private var picture_holder:Sprite = new Sprite(); //new variable addChild(picture_holder);//in the constructor picture_holder.addChild(picture_loader);//in the get_next_picture function while(picture_holder.numChildren>2){//at the end of the get_next_picture function picture_holder.removeChildAt(0); }[/as]

The loader objects we remove will be garbage collected since they lost any reference in the code. Now let’s make our timer more accurate. We can just get the current time and use our timer to check when the targeted time occurs and then trigger our function, that way we’ll get a more accurate timing.

[as] private var current_time:Number;//create a new variable to hold the current time display_timer = new Timer(50); //we place this inside our get_next_picture function current_time = getTimer(); //we place this inside our get_next_picture function display_timer.addEventListener(TimerEvent.TIMER, check_target_time); //we place this inside our get_next_picture function display_timer.start(); //we place this inside our get_next_picture function [/as]

and here is our new function:

[as] private function check_target_time(e:TimerEvent):void{ if(getTimer()>current_time+(display_time*1000)){ //if the actual time passes the initial time + the target time //(+5 seconds) then it's time to get a new picture display_timer.removeEventListener(TimerEvent.TIMER, check_target_time); get_next_picture(); } }[/as]

Now we got an accuracy of more or less 50 milliseconds. You can get an even better accuracy by reducing the number passed to the timer (new Timer(50)). Now we need to change our code logic so that as soon as we display a picture, we start to load the next one so when the time comes to display it we can just do so. Let's see that in the next page.


Loading Pictures (or How do I time this?) part 3

We need to handle the case when it’s time to display the picture but the picture is not done loading. So even though it’s time to display it, we need to wait somehow until we actually can display it. So instead of having one function loading and displaying pictures we should have two separate functions instead so we can control what happens when. Also the first picture is a special case since we need to display it as soon as we can, so we need a special way to handle it. So let’s get started.

[as] private var isPictureReady:Boolean; //a new boolean variable to keep track of the loading state of the picture private var isDisplayTime:Boolean; //a new boolean variable to keep track of the timer state[/as]

We changed our completeHandler function, we now load directly the first picture and then let the engine load and time the next ones:

[as] private function completeHandler(e:Event):void{ xml_pictures = XML(xml_loader.data); number_of_picture = xml_pictures.PICTURES.PICTURE_PATH.length(); display_time = 5; picture_loader =new Loader(); //create a new loader picture_loader.load(new URLRequest(xml_pictures.PICTURES.PICTURE_PATH[0].toString())); //load the first picture picture_loader.contentLoaderInfo.addEventListener(Event.COMPLETE,display_picture); // add an event so we know when the picture is ready current_index++; //increase already our current_index so we’ll load //the second picture next time }[/as]

We create a display_picture function which will set the current time, add the picture to the display list and start the cycle over:

[as] private function display_picture(e:Event = null){ current_time = getTimer(); //our timing starts from here picture_holder.addChild(picture_loader); //show the picture while(picture_holder.numChildren>2){ //we only need two pictures at a time picture_holder.removeChildAt(0); } get_next_picture(); //start loading the next picture }[/as]

And of course our revisited get_next_picture function:

[as] private function get_next_picture():void{ isPictureReady = false; //set the boolean to false isDisplayTime = false; //set the boolean to false if(current_index == number_of_picture){ current_index = 0; } display_timer = new Timer(25); //create a new timer display_timer.addEventListener(TimerEvent.TIMER,check_target_time); //check the timing display_timer.start(); //start the timer picture_loader = new Loader(); //create a new loader picture_loader.load(new URLRequest(xml_pictures.PICTURES.PICTURE_PATH[current_index].toString())); //load the picture picture_loader.contentLoaderInfo.addEventListener(Event.COMPLETE,picture_ready); //when the picture is loaded run this new function picture_ready current_index++; }[/as]

The new picture_ready function:

[as] private function picture_ready(e:Event){ isPictureReady = true; //the picture is loaded so set the boolean to true if(isDisplayTime){ //if this boolean is true then that means the timer ran out //so display the picture display_picture(); } }[/as]

and finally the modified check_target_time function:

[as] private function check_target_time(e:TimerEvent):void{ if(getTimer()>current_time+(display_time*1000)){ display_timer.removeEventListener(TimerEvent.TIMER, check_target_time); isDisplayTime = true; //check this boolean to true so we know it's time to display the picture if(isPictureReady){ //if the picture is ready then display it display_picture(); } } }[/as]

Our slide show now handles in a perfect timely manner the displaying of the pictures. Now of course we still have a bit of work to do. We set the target time in the code so we need to change that so we can pass the time we want from the XML. We also do not handle the case where a picture does not load (probably because of a wrong path). Finally we probably should wrap all that up in a class. So let’s finish that up in the next page.


Loading Pictures (or How do I time this?) part 4

Passing the time from the XML to the application is really not that hard, we can set an attribute in our root XML tag and grab it to set our target time. We could also define a new tag in our XML to hold the time value but let’s stick with an attribute here. Here is our modified XML slide show root tag:

<SLIDE_SHOW DisplayingTime='4'>

Let’s go back in our completeHandler function and grab this value:

[as] display_time = Number(xml_pictures.attribute("DisplayingTime")) || 5; //set our time from the attribute or set it //to 5 if the value is not valid [/as]

That was hard I know. Let’s assume that the first picture is required to load fine (we could handle that too but I leave it up to you to implement if you want) so now let’s handle any following picture loading error by adding an IOError event to our loader:

[as] picture_loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,save_slide_show);[/as]

and our new save_slide_show function just saves the day:

[as] private function save_slide_show(e:IOErrorEvent):void{ picture_loader = new Loader(); picture_loader.load(new URLRequest(xml_pictures.PICTURES.PICTURE_PATH[current_index].toString())); picture_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, picture_ready); picture_loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, save_slide_show); current_index++; }[/as]

That’s it, any wrong paths will just be skipped, now remember you might not want that since if you have a wrong path the slide show will still be working fine and you might never realize that you are missing a picture. Also you might want to handle the case when it’s time to display a picture but the picture is not fully loaded yet. So if you want to display any kind of preloader when that happens you’ll do it here:

[as] private function check_target_time(e:TimerEvent):void{ if(getTimer()>current_time+(display_time*1000)){ display_timer.removeEventListener(TimerEvent.TIMER, check_target_time); isDisplayTime = true; //check this boolean to true so we know it's time to display the picture if(isPictureReady){ //if the picture is ready then display it display_picture(); }else{ //start your preloader here } } }[/as]

then you remove your preloader here:

[as] private function picture_ready(e:Event){ isPictureReady = true; //the picture is loaded so set the boolean to true if(isDisplayTime){ //if this boolean is true then that means the timer //ran out so display the picture display_picture(); //remove your preloader here } }[/as]

Now let’s wrap all that up into a class. We’ll call it TimeManager class and beside the fact that it will keep our code organized, it will also make it simpler to add more functionality later on. So you’ll find most of the code you saw so far in this new class the only differences being that this class dispatches an event that we use to add new pictures (that we get from this class) to the display list in our document class. That’s it for this article, next time we will resize all these pictures (and the swf itself!).