Linked In Profile 
Posted 17 August 2008
51 Comments

As I venture into the depths of AS3 – I find myself pulling my hair out yet again (and those of you who know me know I have no hair left, so that’s not a good thing). Loading multiple images in Flash is (in my opinion) one of the most basic tasks you should be able to perform – and yet I have struggled with this for the past several hours in AS3. This will be basic knowledge to many of you, but bare with me.

So I need to load a series of JPGs to my movie. Easy right? Right. Back in the days of AS2, all you needed was a simple MovieClipLoader to get all the information you needed, and then some. And what was great about that, is you could re-use it for anything you needed. For example:

var mcl:MovieClipLoader = new MovieClipLoader();
var mcl_listener:Object = new Object();
mcl.addListener(mcl_listener);

mcl_listener.onLoadInit = function(mc:MovieClip) {
	// mc has been initialized and is ready to go
}

Now, anytime you need to load a series of images, you simply use a loop:

for (var i:Number = 0; i < 5; i++) {
	mcl.loadClip(yourImgArray[i]);
}

Not bad right? Well, there is one problem with this method. The problem was that if Flash failed to find an image or the URL was incorrect, it would stop completely unless you specified a way to handle the errors (which most developers don't bother doing).

Now to AS3
AS3 handles loading of assets quite differently. It now uses the Loader class which holds all the same information as the MovieClipLoader in AS2, and much more. What I found completely mind-boggling, however, is the fact that in AS3, instead of using one loader class for all images, you need a loader class for each item – which to me feels redundant, but after some thought, I realize this will probably eliminate the error problem I told you about in AS2. See, since each asset requires its own Loader, then if it fails, it only affects that one asset. Instead of the whole script crapping out.

Now...I have no clue if this is all accurate...this is simply my own conclusion – which makes sense in my mind – even though it took me several hours to figure it out. So, without further ado:

var imgPath:Array = ["pic1.jpg", 
         "pic2.jpg", 
         "pic3.jpg", 
         "pic4.jpg", 
         "pic5.jpg"];
var img_arr:Array = new Array();

for (var n:int = 0; n < imgPath.length; n++) {
     var imgLoader:Loader = new Loader();
     imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
     imgLoader.load(new URLRequest(imgPath[n]));
}

function loadComplete(e:Event):void {
     img_arr.push(e.target.content);

     if (imgPath.length == img_arr.length) {
	for (var i:int = 0; i < img_arr.length; i++) {
		addChild(img_arr[i]);
	}
     }
}

Is it possible to reuse one Loader class for all images? As far as I can tell...no. But I may be mistaken – so far though, all evidence points to the need of using individual loaders. Having said that, many folks rave about this bulk loader class written by Arthur Debert (which I haven't tried yet, but looks very solid).

So there you have it folks. I'm positive many of you will have already figured this out eons ago, but for those of you in the same boat as I (resisting change until the very last possible moment), I hope you find it useful.

[update:] The hair-pulling continues. Even though the above method does work, there is one detail that I did not count on. Because each loader acts independently from the others, they also complete the load at different times. So...if you're trying to load images in a specific sequence, you will need to rethink how you go about doing this. I had to rewrite the entire loading process just to get them to load in sequence and got it to work - but it feels kludgy. Anyone have good tips for this?

51 Responses to “Loading Multiple Images in AS3”

  1. Francis Says:


    Dude – thank you.
    Very helpful.
    Much appreciated.

  2. Mark Parson Says:


    Hey David, I recently wrote a light weight loading class to address this very issue (loading a large set of images in sequence).

    The implementation looks like:

    var ldr:ImageLoader = new ImageLoader( "some/image.jpg", myHolderSprite );
    ldr.addEventListener( Event.COMPLETE, _handleImageLoaded );
    ldr.addItemToLoadQueue();

    Let me know if you want the source.

  3. Mike Says:


    While this seems obvious now that I spent over an hour trying to figure this out, you certainly saved me additional hours of time.

    Thanks!

  4. Brian Sexton Says:


    Now try loading multiple images into different dynamically created display objects. Ugh.

  5. patrick Says:


    pretty sweet!
    I tried loading the images from an xml file and it didn’t work until i declared the content to be a Bitmap like

    img_arr.push(Bitmap(e.target.content));

    It might be common knowledge to you guys but i didn’t know this til now.

    thanks!

  6. nate Says:


    Looks like i’ve been mucking through the same thing that you went through…

    I’m able to get the last image in my array to load into the last dynamically attached clip, but none of the previous clips load their images.

    this leads me to believe that “var imgLoader:Loader = new Loader();” is over-writing/consuming the instance of the loader class with each iteration of the loop.

    anyone else experiencing this?

  7. D Molanphy Says:


    @nate: Is your “new Loader()” code inside or outside of the loop? Remember, you need a loader for each instance, so it needs to be inside the loop. Essentially, you have to create a new Loader for every item you want to load in.

  8. Randall Says:


    David: did you ever figure out a non-kludgy way to make the items load in sequence? And actually, kludgy or not I’d like to see how you rewrote it. I’m no expert at this but what you’re attempting to do here is exactly what I need. Thanks a lot for struggling with it and hopefully saving the hairs of others.

    Also, Mark: Yes, I’d be interested in seeing your source, thanks.

  9. D Molanphy Says:


    @Randall: The way I got around it was instead of loading all images at once in the for loop, I paced it to load one by one using a counter variable. So basically:
    if (count < image_arr.length) {
    ...code to load image here
    count++
    }

    Another way (and probably the correct way) is to make a class that handles the loading of just one image, and run that class through the array.

    I have tried both approaches, and both work well, though I do feel like the second approach is more "correct" – if that's even possible.

  10. Randall Says:


    Thanks a lot David. Unfortunately I’m still not familiar enough w/ AS to work out what you’re saying (I’m trying the first method you mention). The original version of your code works for me but seems to kick out a random array order, as you mentioned. But I haven’t been able append the code with your above comment in a way that works. Do you mind showing the entire piece of corrected code? Sorry to be a pain about this! (I’m unfamiliar with making classes, which is why I’m sticking to your first suggestion.)

  11. Ruben Goethals Says:


    Hey, boys & girls,
    You CAN load multiple images with one Loader!
    Simply don’t add the loader to anything, in other words don’t do this:
    mImgLdr=new Loader();
    myTarget.addChild(imgLoader);
    But instead in the onComplete Event, you add the loaded content to wherever you want, and do this as multiple times as you want, like this:
    mImgLdr=new Loader();
    bigImgLdr.contentLoaderInfo.addEventListener(Event.COMPLETE, mImgOnComplete);
    function mImgOnComplete(e) {
    myTarget.addChild(e.currentTarget.content);
    //here would be your next mImgLdr.load(img[I++]) if you want to load images in sequence!
    }
    mImgLdr.load(img[0]);//load first image

    And voilá!
    ( Audience banner: Applaud now! ;-) )

  12. Mike Walton Says:


    Here’s a recursive function that will load your images sequentially, each image displaying after it finishes download:

    var imagePath:Array = ["image1.jpg", "image2.jpg", "image3.jpg"];
    var imageArr:Array = new Array();
    loadImage(imagePath[0]);

    function loadImage(img:String):void {
    var imageLoader:Loader = new Loader();
    imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
    imageLoader.load(new URLRequest(imagePath[0]));
    }

    function loadComplete(e:Event):void {
    imageArr.push(e.target.content);
    imagePath.shift();
    addChild(imageArr[imageArr.length - 1]);
    if (imagePath.length > 0) {
    loadImage(imagePath[0]);
    }
    }

    Here’s one that does the same thing, displaying in proper order but displaying simultaneously:

    var imagePath:Array = ["image1.jpg", "image2.jpg", "image3.jpg"];
    var imageArr:Array = new Array();
    loadImage(imagePath[0]);

    function loadImage(img:String):void {
    var imageLoader:Loader = new Loader();
    imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
    imageLoader.load(new URLRequest(imagePath[0]));
    }

    function loadComplete(e:Event):void {
    imageArr.push(e.target.content);
    imagePath.shift();
    if (imagePath.length > 0) {
    loadImage(imagePath[0]);
    }
    else {
    for (var i:int = 0; i < imageArr.length; i++) {
    addChild(imageArr[i]);
    }
    }
    }

    The only catch is that if you get an incorrect image path or one fails to load for some reason, it’ll stop the recursive function unless you add error handling which, as David mentioned, most developers don’t bother doing. Hope these help.

  13. Randall Says:


    Hey, voila! Mike’s last piece of code was exactly what I was looking for and works like a charm. Thanks Mike, and to everyone else who responded!

  14. Mark Parson Says:


    If anyone would like our classes for loading multiple images in a set sequence, they can be downloaded here:

    http://www.delorum.com/code/

  15. D Molanphy Says:


    Very cool guys! Thank you!

  16. numediaweb Says:


    in order for this to works (stor images on array and control them with ease) you got to declare em as aBimaps; http://www.flashandmath.com/bridge/spintake2/loader.html

  17. jon Says:


    dont forget to remove your load event listeners!

  18. Brian Hodge Says:


    Mike Walton definitely has the proper approach, fill an array with the paths of the data you wish to load. Create a function that handles the loading of these assets by first checking if the array containing the paths is greater than zero in length; If so, load the first item which is at [0] index, then shift() to remove [0]. In each the Event.COMPLETE handler merely needs to call the first function noted, which checks the array if it still has items, then if so begins the load sequence again. The beauty with this approach is that not all of the assets are loading at the same time, and that they can be properly positioned based on their width and height, because those values are available in the Event.COMPLETE handler. Sorry if this was redundant but I struggled back in the day with ideas like this. The natural tendency is a for loop, but that just sends things off and running with no control and possibly lack of performance.

  19. shellen Says:


    Hi,
    The codes is really cool, I’m able to load the images. But just wonder if I am able to arrange the images because currently what I have now is the images overlapping each other.

  20. OleMedia Says:


    Brian, for those who are learning AS3, can you post an example of how to position each image? like a grid for example. Also, how do you reference each image to an XML file to load a bigger image or get more details?

  21. OleMedia Says:


    Brian, following Mike Walton’s example and for those who are learning AS3, can you post an example of how to position each image? like a grid for example. Also, how do you reference each image to an XML file to load a bigger image or get more details?

  22. Brian Hodge Says:


    OleMedia,

    Well you question is perfect because the very reason I even posted was that I wanted others to really grasp Mike’s use of an array; especially when working with XML.

    Loading XML is very easy, in fact I have since written a class to simplify it even more, but I really stress that before using classes like those, learn the base (native) classes which would be used to build the custom class. That being said, lets abstract this out a little. For XML, you require a URLLoader Object, a URLRequest object and a XML Object. URLLoader and URLRequest reside in the flash.net package and XML is native. It is ever so important to learn what is available to you through the AS3 API, please check it out, especially if your trying to wrap your head around events :) (THIS IS ON MY TOOLBAR FAVORITES)http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/ .

    Now ill layout a minimal implementation of a Document class so I can explain about why I wanted to point out Mikes approach for being proper.

    package
    {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.net.URLRequest;
    import flash.net.URLLoader;

    public class DocumentClass extends Sprite
    {
    //I precede private variables with an underscore “_”
    private var _xmlRequest:XMLRequest;
    private var _xmlLoader:XMLLoader;
    private var _xml:XML;

    public function DocumentClass():void
    {
    _xmlRequest = new XMLRequest(‘path/to/file’);
    _xmlLoader = new URLLoader();
    _xmlLoader.addEventListener(Event.COMPLETE, _onXMLLoaded);

    _xmlLoader.load(_xmlRequest);
    }
    private function _onXMLLoaded(e:Event):void
    {
    _xml = new XML(e.target.data);
    }
    }
    }

    Now that _xml has data, I have seen often where people will setup a for loop to iterate the the array and to load image based on a string pulled from each iteration. For simple learning this is ok, but what if you want the second image to start off where the prior ends? Though this could be handled after all are finished loading we could call a function to reposition, but there is still a downside, the assets all loaded at the same time.

    We will still iterate through the XML, but what should be done is to fill an array with the XML data. This is where Mike Walton’s example needed to be pointed out for it correctness.

    First a function is created to handle loading an image from the array. Each time this function is called the item at array[0], the first item in the array, is used to start the load of some asset, then it is removed from the array using the Array.shift() method which simply removes the first item in an array and returns it. The function created to load images is also responsible for assigning an event listener to _xmlLoader object for Event.COMPLETE which is fired when it has received all data. Once Event.COMPLETE has fired for this asset, we can finally access properties like WIDTH and HEIGHT, maybe now your starting to see the benefit here as opposed to a quick and easy for loop.

    You will notice in Mike’s implementation the the Event.COMPLETE event listener calls the loadImage function which then has a condition inside which decides if it is to load another asset. Each time loadImage runs it removes an item from the array, if the array is empty the condition fails, no asset is loaded so no event is fired thus ending the recursive-like calls.

    Now to position an object based on the last, we need to know the last object’s position and size, in this case its x value, and its width. If the assets are top-left registered than we can base a new objects x off of the last objects x + its width and if you need a lil space you add whatever else you want on. It is easy as creating two variables, lastX and lastWidth, then in the Event.COMPLETE listener function you have a condition like this:

    private function _onImageComplete(e:Event):void
    {
    //IF THEY HAVE BEEN SET (NOT NULL);
    if(lastX && lastWidth)
    {
    e.target.x = lastX + lastWidth;
    }
    //AFTER USE WE SET THE NEW VALUES
    lastX = e.target.x;
    lastWidth = e.target.width;

    loadImage();
    }

    So first we check if lastX and lastWidth have a value, if not, it is the first object and it should be at 0,0 unless otherwise specified. The condition fails skipping the if block and moving onto setting the lastX and lastWidth to the current object’s values, then it calls loadImage which starts the process all over. This continues until every last item in the array is used and removed cause the loadImage function to fail.

    HOPE THIS HELPS!!! ITS TOO LATE!!!
    Brian Hodge

  23. D Molanphy Says:


    Hey Brian: Great explanation. Thank you. Quick q for you. Why is it necessary to extend the Sprite class in your XML loader? Can you explain that part please?

  24. Brian Hodge Says:


    The reason that the DocumentClass extends sprite is because any Document class (The OOP entry-point) must extend MovieClip or Sprite because it contains visible assets and to add them to the display list and to be able to have children of their own, they would need to be DisplayObjectContainers which Sprite and MovieClip are themselves extended from at some point; A DisplayObject is merely an object that can be added to the display list, but a DisplayObjectContainer can hold children (DisplayObjects) of its own, and itself also be added to the DisplayList.

    In other words, for our assets to be seen, those inside loaded directly into the document class (root), or those instantiated in the document class that have their own scope, the base class (DOCUMENT CLASS) must itself be a DisplayObjectContainer so that it to can hold children and be added to the display list itself.

    I cannot stress how important it is to start looking through the AS3 API and see what options are available to you, what objects extend others, and Events, sigh, events had me in the API often :) .

    Brian Hodge

  25. D Molanphy Says:


    OH! oops. My bad. Should’ve paid more attention. Didn’t realize this was the DocumentClass. :T

  26. Josh Babier Says:


    Mike Walton & Brian Hodge,

    Thank you.

    Very clear stuff. Great explanations. Very useful. Thanks, you have demystified alot for me. I will try to put it all into action.

  27. Scott Shaper Says:


    Just wanted to thank Mike Walton and the others who contributed. I have been working on this for hours and his code helped me solve the problem. I had to load images from an xml file, which added a small twist of putting it into an array.

    Thank you

  28. dreamKeeper Says:


    Hey guys, I have a problem with XML. I’m working in AS3 and i have an XML file that contains the path for more than 50 images that have to be loaded as in a slideshow. When you click “next” the next image must be loaded and replace the previous one. How can I do this??? I tried and tried, but nothing works well… CAN YOU HELP? :D Thank you ;)

  29. Brian Hodge Says:


    well lets say you loaded xml into an object properly with URLLoader as I will show below. You will need a variable to hold the current images index, so you know what to load when you click a next or back button.

    Example: XML

    private var _index:int; //defaults to 0
    private var _xml:XML;

    private var _urlLoader:URLLoader = new URLLoader();
    _urlLoader.addEventListener(Event.COMPLETE, _onXMLComplete, false, 0, true);

    private var _request:URLRequest = new URLRequest(“path/to/xml.file”);

    _urlLoader.load(_request);

    private function _onXMLComplete(e:Event):void
    {
    _xml = e.target.data;
    _loadImage();
    }
    private function _loadImage():void
    {
    var loader:Loader = new Loader();
    var request:URLRequest = new URLRequest(_xml.images.image[_index]); //index is still 0, so calls first image.
    loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, _onImageProgress);
    loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onImageComplete);

    private function _onImageProgress(e:ProgressEvent):void
    {
    trace(e.bytesLoaded / e.bytesTotal);
    }
    private function _onImageComplete(e:Event):void
    {
    //image load completed
    }

    private function prevImage():void
    {
    if(_index > 0)
    {
    _index–;
    _loadImage();
    }
    }

    private function nextImage():void
    {
    if(_index < _xml.images.image.length() – 1)
    {
    _index++;
    _loadImage();
    }
    }
    //
    }

    A mouse down on a previous or next button, could call the nextImage()or prevImage() function, it checks if we are at either of the bounds, 0, or the last image source provided by the xml object, if not, it makes the necessary change to the index integer, and then calls _loadImage() private function again. Sorry that I didnt write the package or class parts, but I was trying to save space.

    Hope this helps, cheers.

  30. Brian Hodge Says:


    *FIX – sorry hate writing code in these things

    private function _loadImage():void
    {
    var loader:Loader = new Loader();
    var request:URLRequest = new URLRequest(_xml.images.image[_index]); //index is still 0, so calls first image.
    loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, _onImageProgress);
    loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onImageComplete)
    loader.load(_request);
    //Inside of _onImageComplete() is where you would add the newly loaded image to the display list.
    }

  31. dreamKeeper Says:


    Thank you very much! I think it’ll work!

  32. Brian Hodge Says:


    hehe it will work. I wanted to add then when you get in a crunch like this, it’s best to step back and ask yourself, what do I need to do, and what do I have to do it.

    You want to pinpoint 1 picture each time out of many. This is why holding an index is a good idea. The XML you loaded in has a method to tell us just how many of a particular element there is in the xml “length()”. So if we manipulate index, within the constraints of zero and the length found of the particular XML elements, we have an easy control for navigating through the images.

    It is always good to abstract things before you start a project. Get a piece of paper, and think to yourself, What are the key things that need to happen here.

    1. We need to be able to click next and back buttons and it respond accordingly.
    2. It needs to load an image from the XML using the current index.
    3. It needs to check that index is greater than 0, and less than the total images in the XML object.

    To break things down to their base requirement, you can attack those things with much more thought. Any other remaining code merely help setup the above, or compliments it.

  33. Brian Hodge Says:


    *FIX #2

    if(_index < _xml.images.image.length() – 1)

    should be

    if(_index < _xml.images.image.length())

    Sorry, it was late. :)

  34. dreamKeeper Says:


    Hey, thanks a lot, I just started working on it, so I hope it’ll work. I also have a page with 16 images that must be loaded together, each picture ina different place on the screen. I tried this code but it doesn’t work… What could be the problem?

    function LoadXML(e:Event):void
    {
    xmlData = new XML(e.target.data);
    ParseXML(xmlData);
    }

    var imgX:Array = new Array(350, 500, 700);
    var imgY:Array = new Array(350, 400, 550);
    var imgWidth:Array = new Array(200, 500, 350);
    var imgHeight:Array = new Array(300, 650, 200);

    function ParseXML(clientsInput:XML):void
    {
    var clientsList:XMLList = clientsInput.client;

    var imageList:XMLList = clientsInput.images.image;

    var thumbnails:Bitmap;

    var thumbLoader:Loader = new Loader();

    for(var c:Number = 0; c < xmlData.client.length(); c++)
    {
    clients.appendText(xmlData.client[c].name.text() + "\r\n");

    var imgName = xmlData.client[c].thumbImage.text();

    thumbLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, ThumbLoad);
    thumbLoader.load(new URLRequest(imgName));

    function ThumbLoad(e:Event):void
    { thumbnails = Bitmap(e.target.content);
    var thumbnailsBitmapData:BitmapData = thumbnails.bitmapData;
    thumbnails.smoothing = true;
    thumbnails.width = imgWidth[xmlData.client[c].screenPosition.text()];
    thumbnails.height = imgHeight[xmlData.client[c].screenPosition.text()];
    thumbnails.y = imgY[xmlData.client[c].screenPosition.text()];
    thumbnails.x = imgY[xmlData.client[c].screenPosition.text()];
    addChild(thumbnails);
    }

    }
    }

  35. dreamKeeper Says:


    I didn’t put the whole code, but I have no errors in it. It’s just that it doesn not happen anything related to this part of the code. I mean no image appears. The images are in the right place, in the folder that is written in the XML file… :-?

  36. Brian Hodge Says:


    Can you show my your complete xml file?

    Have you tried tracing a message from ThumbLoad(), to make sure that it is being called? Are you listening for IO_ERRORs?

  37. dreamKeeper Says:


    ?

    Client1
    1
    “thumbs/thumb1.jpg”
    3
    ?

    “images/image11.jpg”
    “images/image12.jpg”
    “images/image13.jpg”

    ?

    Caption 1 1
    Caption 1 2
    Caption 1 3

    ?

    Client2
    2
    “thumbs/thumb2.jpg”
    4
    ?

    “images/image21.jpg”
    “images/image22.jpg”
    “images/image23.jpg”
    “images/image24.jpg”

    ?

    Caption 2 1
    Caption 2 2
    Caption 2 3
    Caption 2 4

    ?

    Client3
    0

    2
    ?

    “images/image31.jpg”
    “images/image32.jpg”

    ?

    Caption 3 1
    Caption 3 2

    etc.

  38. dreamKeeper Says:


    Sorry…

  39. dreamKeeper Says:


    how can i write it put it here without changing it like the one above? :D

  40. dreamKeeper Says:


    could you give me your e-mail address so that I can send you the code and the xml? it would really help me :D just in case you don’t mind ;)

  41. John Vargo Says:


    Brian et al., thanks for the code, and the insight.

    I am stuck on one point. I see your code to set the x and y for an image, but how can I set the x and y for an image that is added to the stage using addChild()?

    I’m using the code you posted previously:

    var imagePath:Array = new Array(“image1.png”, “image2.png”);

    var imageArr:Array = new Array();

    loadImage(imagePath[0]);

    function loadImage(img:String):void {
    var imageLoader:Loader = new Loader();
    imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
    imageLoader.load(new URLRequest(imagePath[0]));
    }

    function loadComplete(e:Event):void {
    imageArr.push(e.target.content);
    imagePath.shift();
    addChild(imageArr[imageArr.length - 1]);
    if (imagePath.length > 0) {
    loadImage(imagePath[0]);
    }
    }

    I don’t see an “object” that I can set the x and y for.

  42. Brian Hodge Says:


    You would handle this inside of loadComplete() function as so.

    private function loadComplete(e:Event):void
    {
    var bmp:Bitmap = e.target.content as Bitmap;
    if(container.numChildren > 0) bmp.x = container.getChildAt(container.numChildren – 1).x + container.getChildAt(container.numChildren – 1).width + 2;
    container.addChild(bmp);
    }

    Waiting for an image to be completed before making assignments isn’t always required, but it is how I like to do things. Once Event.COMPLETE has fired for a particular image, you have access to properties such as width and height.

    The example I made above checks if it is the second image added to the container, and then it bases its x value off of that of the x and the width of the previous display object.

    If you do not need to know the bitmaps height, then you could just add the loader right after instantiating it.

    brian@hodgedev.com

  43. Brian Hodge Says:


    Again, if you don’t need to know attributes that are only available after lead is complete, you could do the following with your code.

    var imageLoader:Loader = new Loader();
    imageLoader.x = 238;
    addChild(imageLoader);

    All depends on what you want need or prefer. :)

  44. John Vargo Says:


    Thanks Brian, e.target.content is exactly what I was missing. *slaps de head*

  45. Brian Hodge Says:


    It is also important to not that another reason it is good to wait for each to complete their load, then to start next, is that you can actually choke your web server and timeout image calls.

    Check out the following answer I gave on stack overflow.
    http://stackoverflow.com/questions/1478488/as3-preloader-occasionally-wont-load-images-or-external-swf/1481456#1481456

  46. Brian Hodge Says:


    *It is also important to note

  47. Brian Hodge Says:


    No problem, glad I could help.

    Dreamkeeper, i gotta step out, but feel free to email that xml to me and I will have a look. You may send your source files too incase the problem is there.

    http://blog.hodgedev.com
    http://www.hodgedev.com
    brian@hodgedev.com

  48. John Vargo Says:


    Brian, on to my next problem.

    I need next/back buttons to tween the images left or right off the stage. Since these images are bitmaps on the timeline, I don’t have any way of accessing them to apply tweens. Is there a way I can do this?

    My code so far:

    var imagePath:Array = new Array(“images/interactive_media.png”, “images/trade_media_paid.png”);
    var captions:Array = new Array(“This is image 1″, “This is image 2″);
    var imageArr:Array = new Array();
    var xMulti:int; // a simple counter
    var curIndex:int; // navigation index
    loadImage(imagePath[0]);
    function loadImage(img:String):void {
    var imageLoader:Loader = new Loader();
    imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
    imageLoader.load(new URLRequest(imagePath[0]));
    }
    function loadComplete(e:Event):void {
    var bmp:Bitmap = e.target.content as Bitmap;
    imageArr.push(e.target.content);
    imagePath.shift();
    e.target.content.x = 0 + (xMulti * 900) + (450 – (bmp.bitmapData.width / 2));
    xMulti++;
    e.target.content.y = 300 – (bmp.bitmapData.height / 2);
    bmpMc.addChild(bmp);
    if (imagePath.length > 0) {
    loadImage(imagePath[0]);
    } else {
    drawCaptionRect();
    }
    }
    function drawCaptionRect():void {
    var captionRect:Sprite = new Sprite();
    addChild(captionRect);
    captionRect.graphics.lineStyle(0,0×000000);
    captionRect.graphics.beginFill(0×000000, 0.60);
    captionRect.graphics.drawRect(0,600,900,75);
    captionRect.graphics.endFill();
    captionRect.name = “captionBackground”;

    Tweener.addTween(captionRect, {y:-75, time:1.0, transition:”easeOutSine”, onComplete:addCaption, onCompleteParams:[captions[0]]});
    }

    function addCaption(msg:String):void {
    var txt:TextField = new TextField();
    addChild(txt);
    txt.name = “dynamicTextField”;

    txt.text = msg;
    txt.width = 900;
    txt.height = 70;
    txt.x = 0;
    txt.y = 530;
    txt.selectable = false;
    txt.border = false;
    txt.borderColor = 0xCCCCCC;
    txt.antiAliasType = “advanced”;
    txt.alpha = 0;
    var format:TextFormat = new TextFormat();
    format.font = “Verdana”;
    format.color = 0XFFFFFF;
    format.size = 14;
    format.align = “center”;

    txt.setTextFormat(format);
    Tweener.addTween(txt, {alpha:1, time:0.5, transition:”linear”});
    }
    function getNext(e:Event):void {
    trace(“forward”);
    }

    function getPrev(e:Event):void {
    trace(“back”);
    }

    forward_btn.addEventListener(MouseEvent.CLICK, getNext);
    back_btn.addEventListener(MouseEvent.CLICK, getPrev);

  49. Brian Hodge Says:


    Ok, so you are adding each bmp, to bmpMc, so we now have a way to access things.

    I would use tweenlite to move the container. If the container starts at x: 0, which is the left most of the stage, and we move the entire container left based on the x: of one of it’s children, which is found with curIndex;

    function nextImage():void
    {
    if(curIndex < imagePath.length)
    {
    curIndex++;
    TweenLite.to(bmpMc, 1, {x: -bmpMc.getChildAt(curIndex).x}); //Notice the "-" infront of bmpMc,

    if(curIndex == imagePath) rightArrow.visible = false; //If at end, remove the next button as not to confuse people.
    }
    }

    Hope this helps, was in a rush.

  50. John Vargo Says:


    Thanks again for the help Brian and OP. I was able to get it all worked out.

    I’m going to post my final code here for posterity, in case it helps anyone else who happens upon this thread. :)

    This code relies on a button in the library with class NavButton, and a font in the library with class Myriad. It also requires the tweener lib. Cheers!

    import fl.transitions.*;
    import fl.transitions.easing.*;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Loader;
    import flash.display.LoaderInfo;
    import flash.display.Sprite;
    import flash.net.URLRequest;
    import flash.geom.Rectangle;
    import flash.display.DisplayObject;
    import flash.display.DisplayObjectContainer;
    import flash.geom.ColorTransform;
    import caurina.transitions.*;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    import flash.text.Font;
    import flash.text.AntiAliasType;

    var imagePath:Array = new Array(“images/image1.png”, “images/image.png”);
    var captions:Array = new Array(“This is image 1.Yes we have line breaks.”, “This is image 2″);
    var headline:String = new String();
    headline = “title”;
    var subhead:String = new String();
    subhead = “a subheading”;
    var imageArr:Array = new Array();

    var font:Myriad = new Myriad();

    var imageLen:int = imagePath.length;
    var xMulti:int; // this is a current instance multiplier
    var viewIndex:int; // this is the slide we are viewing

    var imageMC:Sprite = new Sprite();
    imageMC.name = “imageMC”;

    var captionMC:Sprite = new Sprite();
    captionMC.name = “captionMC”;
    addChild(captionMC);

    drawCaptionRect();
    drawHeaderRect();
    drawHeadings();

    function drawCaptionRect():void {
    var captionRect:Shape = new Shape();
    captionRect.graphics.beginFill(0xFF6633, 0.80);
    captionRect.graphics.drawRect(10,345,432,40);
    captionRect.graphics.endFill();
    addChild(captionRect);
    }

    function drawHeaderRect():void {
    var headerRect:Shape = new Shape();
    headerRect.graphics.beginFill(0×000000, 1);
    headerRect.graphics.lineStyle(1, 0xFF6633);
    headerRect.graphics.drawRect(-1,-1,454,50);
    headerRect.graphics.endFill();
    addChild(headerRect);
    }

    function drawHeadings():void {
    var headlineTxt:TextField = new TextField();
    headlineTxt.text = headline;
    headlineTxt.x = 20;
    headlineTxt.y = 6;
    headlineTxt.width = 400;
    headlineTxt.selectable = false;
    headlineTxt.border = false;
    headlineTxt.antiAliasType = “advanced”;
    headlineTxt.embedFonts = true;

    var headlineTxtFormat:TextFormat = new TextFormat();
    headlineTxtFormat.font = font.fontName;
    headlineTxtFormat.color = 0XFFFFFF;
    headlineTxtFormat.size = 18;
    headlineTxtFormat.align = “left”;

    headlineTxt.setTextFormat(headlineTxtFormat);

    var subheadTxt:TextField = new TextField();
    subheadTxt.text = subhead;
    subheadTxt.x = 20;
    subheadTxt.y = 26;
    subheadTxt.width = 400;
    subheadTxt.selectable = false;
    subheadTxt.border = false;
    subheadTxt.antiAliasType = “advanced”;
    subheadTxt.embedFonts = true;

    var subheadTxtFormat:TextFormat = new TextFormat();
    subheadTxtFormat.font = font.fontName;
    subheadTxtFormat.color = 0XFFFFFF;
    subheadTxtFormat.size = 14;
    subheadTxtFormat.align = “left”;

    subheadTxt.setTextFormat(subheadTxtFormat);

    addChild(headlineTxt);
    addChild(subheadTxt);
    }

    if (imageLen > 0) {
    loadImage(imagePath[0]);
    addCaption(captions[0]);
    }

    function loadImage(img:String):void {
    var imageLoader:Loader = new Loader();
    imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
    imageLoader.load(new URLRequest(img));
    }

    function loadComplete(e:Event):void {
    var bmp:Bitmap = e.target.content as Bitmap;
    imageArr.push(e.target.content);
    imagePath.shift();
    e.target.content.x = 0 + (xMulti * 452) + (226 – (bmp.bitmapData.width / 2));
    xMulti++;
    e.target.content.y = 210 – (bmp.bitmapData.height / 2);
    imageMC.addChild(bmp);
    if (imagePath.length > 0) {
    loadImage(imagePath[0]);
    } else {
    addChild(imageMC);
    addChild(forward_btn);
    addChild(back_btn);
    }
    }

    function addCaption(cap:String):void {
    while(captionMC.numChildren) {
    captionMC.removeChildAt(0);
    }

    var capTxt:TextField = new TextField();
    capTxt.x = 20;
    capTxt.y = 350;
    capTxt.width = 400;
    capTxt.selectable = false;
    capTxt.border = false;
    capTxt.antiAliasType = “advanced”;
    capTxt.condenseWhite = true;
    capTxt.multiline = true;
    capTxt.htmlText = cap;

    var headlineTxtFormat:TextFormat = new TextFormat();
    headlineTxtFormat.font = font.fontName;
    headlineTxtFormat.color = 0XFFFFFF;
    headlineTxtFormat.size = 12;
    headlineTxtFormat.align = “left”;

    capTxt.setTextFormat(headlineTxtFormat);

    captionMC.alpha = 0;
    captionMC.addChild(capTxt);
    addChild(captionMC);
    Tweener.addTween(captionMC, {alpha:1, time:0.25});
    }

    var forward_btn:NavButton = new NavButton();
    forward_btn.x = 442;
    forward_btn.y = 212.5;
    forward_btn.rotation = 180;

    var back_btn:NavButton = new NavButton();
    back_btn.x = 10;
    back_btn.y = 170;

    function getNext(e:Event):void {
    var curX:Number = imageMC.x;
    var maxX:Number = (imageLen – 1) * -452
    var newX:Number = curX – 452;
    if (newX >= maxX) {
    viewIndex++;
    Tweener.addTween(imageMC, {x:newX, time:0.5, transition:”easeOutSine”});
    Tweener.addTween(captionMC, {alpha:0, time:0.25, onComplete:addCaption, onCompleteParams:[captions[viewIndex]]});
    }
    }

    function getPrev(e:Event):void {
    var curX:Number = imageMC.x;
    var maxX:Number = 0;
    var newX:Number = curX + 452;
    if (newX <= maxX) {
    viewIndex–;
    Tweener.addTween(imageMC, {x:newX, time:0.5, transition:"easeOutSine"});
    Tweener.addTween(captionMC, {alpha:0, time:0.25, onComplete:addCaption, onCompleteParams:[captions[viewIndex]]});
    }
    }

    forward_btn.addEventListener(MouseEvent.CLICK, getNext);
    back_btn.addEventListener(MouseEvent.CLICK, getPrev);

  51. Mike Grace Says:


    Thanks for the help. It’s always nice to get help from fellow BYU-I students/alumni. : )