Skip to content

May 13, 2010

4

AS3 xml-slideshow

Goal

In this tutorial we’re trying to create a simple slideshow in AS3, that will give the user the ability to switch back and forth between the images!

You’ll learn how to:

  • read and process xml-data
  • add a description to your image
  • attach a url to it
  • use TweenLite for animation effects

Because it’s based on xml-data it’ll be simple to modify and extend.
The xml could even be generated by a php script of course!

I presume you have basic knowledge of AS3 and how things work so I won’t go into every single detail.

 

Final result

Click on the image to see the final result
final result

 

Getting started

Create a new Flash File with the following settings:

  • Type: AS3
  • FPS: 30
  • Size: 500 x 333 px

and get it saved somewhere!

create an xml-file named data.xml and save it in the same location as your flash-file
lastly create a subfolder named images and put all your images cropped correctly inside!

 
The XML
<?xml version="1.0" encoding="UTF-8"?>

<slideshow>

<image>
	<path>images/image01.jpg</path>
	<description>An old farmer's kitchen</description>
	<url>http://www.fabian-irsara.com/blog/2010/05/as3-xml-slideshow/</url>
</image>

<image>
	<path>images/image02.jpg</path>
	<description>Sweet little kitty</description>
	<url>http://www.fabian-irsara.com/blog/2010/05/as3-xml-slideshow/</url>
</image>

<image>
	<path>images/image03.jpg</path>
	<description>Great sea</description>
	<url>http://www.fabian-irsara.com/blog/2010/05/as3-xml-slideshow/</url>
</image>

<image>
	<path>images/image04.jpg</path>
	<description>Majestically swan</description>
	<url>http://www.fabian-irsara.com/blog/2010/05/as3-xml-slideshow/</url>
</image>

</slideshow>

to explain it shortly:

<?xml version="1.0" encoding="UTF-8"?>

In the first line we simply specify the xml version of our document and set its character encoding to utf-8 (which is good, since AS3 uses utf8-encoding by default)!

<slideshow>
<image>
	<path>images/image01.jpg</path>
	<description>An old farmer's kitchen</description>
	<url>http://www.fabian-irsara.com/blog/2010/05/as3-xml-slideshow/</url>
</image>
...
</slideshow>

Next we’re wrapping all our image data into a slideshow tag. This is a neccessary step to read our data later on in AS3! Inside this slideshow tag we set up all our image-information we need in separate tags (e.g. path sets the location of our image) and wrap it all into a single image tag!

 
The Flash

Back to Flash we’re setting up our scene like the following:

Stage frame 1
flash frame 01

In frame 1 create a static textfield that says reading data or similar to give the user some feedback while we are retrieving the data from our xml file! In this particular example you’ll barely see this first frame, but considering your xml could hold up a whole load of data, that can take a lot of time to load, it’s a good point to give the user at least some feedback to hold up and wait!

Stage frame 2
Flash frame02

In frame 2 we have some several other objects on our stage:

  1. An empty MovieClip, given the instance name imgHolder
    This one will hold up our images later on
  2. Two MovieClips, acting as a Button to switch between our images
      Instance Names:

    • prev_btn for the left one
    • next_btn for the right one
  3. A MovieClip with the instance name description
    having a textfield inside to hold up our description (instance name: txtfield)
 
The ActionScript
stop();

import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
import com.greensock.TweenLite;
import com.greensock.easing.Quint;

var BASE:String = ""; //The path to our swf file, leave blank if unsure
var xmlPath:String = BASE + "data.xml"; //the path to our xml-doc.
var fadeSpeed:Number = 0.5;

var xml:XML; //holds our xml-data
var imgData:Array = new Array(); //holds our raw imageData

var xmlLoader:URLLoader = new URLLoader(); //creating a URLLoader-object
xmlLoader.addEventListener(Event.COMPLETE, readData); //registering a complete-event for it
xmlLoader.load(new URLRequest(xmlPath)); //finally load our xml

function readData(e:Event):void {
	xml = new XML(e.target.data);

	//For each image in our xml, read the data
	for (var i:Number = 0; i < xml.image.length(); i++) {
		imgData.push(new Array()); //insert a new Array to hold our image-data
		imgData[i]["path"] = xml.image[i].path.toString();
		imgData[i]["description"] = xml.image[i].description.toString();
		imgData[i]["url"] = xml.image[i].url.toString();
	}

	//getting to the next step, loading the images
	gotoAndStop(2);
}

View the whole code for frame 1

import flash.display.Loader;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.MovieClip;
import flash.net.URLRequest;

stop();
//initial setup for our controls
next_btn.buttonMode = prev_btn.buttonMode = true;
next_btn.visible = prev_btn.visible = false;
next_btn.alpha = prev_btn.alpha = 0.8;
next_btn.scaleX = next_btn.scaleY = prev_btn.scaleX = prev_btn.scaleY = 0.8;

var actualID:Number = 0;

//getting our images recursively
getImage(0);

function getImage(id:Number):void {
	//ensure there's still an image to load
	if (imgData[id] != null) {
		//setting a text for our preloader
		description.txtfield.text = "loading image " + (id + 1).toString() + "/" + imgData.length.toString();

		//create a new loader object
		var loader:Loader = new Loader();

		//our new image
		loader.load(new URLRequest(imgData[id]["path"]));
		loader.contentLoaderInfo.addEventListener(Event.COMPLETE,
			function(e:Event) {
				imgData[id]["image"] = generateBitmap(e);
				getImage(id + 1);
			}
		);
	} else {
		description.txtfield.text = "";
		next_btn.visible = prev_btn.visible = true;
		next_btn.addEventListener(MouseEvent.ROLL_OVER, highlightBtn);
		prev_btn.addEventListener(MouseEvent.ROLL_OVER, highlightBtn);
		next_btn.addEventListener(MouseEvent.ROLL_OUT, normalBtn);
		prev_btn.addEventListener(MouseEvent.ROLL_OUT, normalBtn);
		next_btn.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void{addImage(actualID + 1);} );
		prev_btn.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void{addImage(actualID - 1);} );

		imgHolder.buttonMode = true;
		imgHolder.addEventListener(MouseEvent.CLICK, openLink);

		//setting up the first image
		addImage(actualID);
	}
}

// creates a bitmap with smoothing turned on!
function generateBitmap(e):MovieClip {
	var tempBMP:Bitmap = new Bitmap(e.target.content.bitmapData, "auto", true);
	var tempMC:MovieClip = new MovieClip();
	tempMC.addChild(tempBMP);
	return tempMC;
}

function highlightBtn(e:MouseEvent):void {
	TweenLite.to(e.target, 0.25, {scaleX: 1, scaleY: 1, alpha: 1} );
}
function normalBtn(e:MouseEvent):void {
	TweenLite.to(e.target, 0.25, {scaleX: 0.8, scaleY: 0.8, alpha: 0.8} );
}

function showDescription(e:MouseEvent):void {
	TweenLite.to(description, fadeSpeed, {alpha: 1} );
}
function hideDescription(e:MouseEvent):void {
	TweenLite.to(description, fadeSpeed, {alpha: 0} );
}
function openLink(e:MouseEvent):void {
	navigateToURL(new URLRequest(imgData[actualID]["url"]), "_blank");
}

function addImage(id:Number):void {
	if (imgHolder.numChildren > 0) { //remove old image
		TweenLite.to(imgData[actualID]["image"], fadeSpeed, {alpha: 0, ease:Quint.easeIn, onComplete:
			function(){imgHolder.removeChildAt(0); addImage(id);}
		} );
	} else {
		actualID = id;
		if (actualID == 0) prev_btn.visible = false;
		else prev_btn.visible = true;
		if ((actualID + 1) == imgData.length) next_btn.visible = false;
		else next_btn.visible = true;

		description.txtfield.text = imgData[id]["description"]

		imgHolder.addChild(imgData[actualID]["image"]);
		imgData[id]["image"].alpha = 0;
		TweenLite.to(imgData[actualID]["image"], fadeSpeed, {alpha: 1, ease:Quint.easeOut} );
	}
}

View the whole code for frame 2

 
Explained

 

Code at frame 1

stop();

import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
import com.greensock.TweenLite;
import com.greensock.easing.Quint;

In these first lines we simply stop our main Timeline and import some libraries we need.
Note the TweenLite library, which is not installed by default.
TweenLite is a very powerful, lightweight tweening engine.
You can get it from greensock!

var BASE:String = ""; //The path to our swf file, leave blank if unsure
var xmlPath:String = BASE + "data.xml"; //the path to our xml-doc.
var fadeSpeed:Number = 0.5;

var xml:XML; //holds our xml-data
var imgData:Array = new Array(); //holds our raw imageData

Here we set up some basic variable definitions!
BASE could be a url to your domain, where the xml-file will be stored!
xmlPath points to the actual xml-file we created
fadeSpeed simply defines how fast (in seconds) our images will fade later on!
xml will hold up the xml-data we’ll read
imgData will be a big array containing all the information we need, like the path to our image and actually even the loaded image itself!

var xmlLoader:URLLoader = new URLLoader(); //creating a URLLoader-object
xmlLoader.addEventListener(Event.COMPLETE, readData); //registering a complete-event for it
xmlLoader.load(new URLRequest(xmlPath)); //finally load our xml

Here we’ll create a URLLoader object named xmlLoader that will load our xml-data!
Of course we have to register an event for our loader, that will trigger to a callback-function called readData, when the file-reading is completed.
At the end we can finally load our xml-file!

function readData(e:Event):void {
	xml = new XML(e.target.data);

	//For each image in our xml, read the data
	for (var i:Number = 0; i < xml.image.length(); i++) {
		imgData.push(new Array()); //insert a new Array to hold our image-data
		imgData[i]["path"] = xml.image[i].path.toString();
		imgData[i]["description"] = xml.image[i].description.toString();
		imgData[i]["url"] = xml.image[i].url.toString();
	}

	//getting to the next step, loading the images
	gotoAndStop(2);
}

This is how our xml-processing looks like!

we create a new xml object containing the data from our loader (e.target delivers the object on your scene that has triggered the event, in this case our object xmlLoader)!

the data property of our loader will give us back the complete set of data we have!

To access xml-nodes we simply call them by their name:
xml.image for example will return all of our image nodes!

Next we create a for loop that’ll pass all our single image tags and give us its data!
Note that length() in this case is a method and has to contain the braces!
inside this loop we store our specific data to our data-array!

At the end we can simply go on to the next frame, where we will process our data!

 

Code at frame 2

//initial setup for our controls
next_btn.buttonMode = prev_btn.buttonMode = true;
next_btn.visible = prev_btn.visible = false;
next_btn.alpha = prev_btn.alpha = 0.8;
next_btn.scaleX = next_btn.scaleY = prev_btn.scaleX = prev_btn.scaleY = 0.8;

Here we simply set some basic properties for our buttons!

var actualID:Number = 0;

and store the actualImage that’ll be attached to our holder.

//getting our images recursively
getImage(0);

function getImage(id:Number):void {
	//ensure there's still an image to load
	if (imgData[id] != null) {

Now the getImage function is recursively. if you don’t know about recursion here’s a very basic tutorial that’ll explain it.

To summarize it: we’ll keep loading an image and counting the index up until there are no images left (which means that our imgData-array returns null for the given index)

Even though this might sound complex it’s actually very simple and has the advantage that you won’t get a hundred image-load request at once, so you can keep them loading and even show content before all are done!

//setting a text for our preloader
		description.txtfield.text = "loading image " + (id + 1).toString() + "/" + imgData.length.toString();

with this piece of code we simply access our description textfield on the stage and use it as a preloading-feedback (e.g. it’ll say loading image 1/5 for the first image).

		//create a new loader object
		var loader:Loader = new Loader();

		//our new image
		loader.load(new URLRequest(imgData[id]["path"]));

Here we create a new Loader object and load our image right away.

    loader.contentLoaderInfo.addEventListener(Event.COMPLETE,
			function(e:Event) {
				imgData[id]["image"] = generateBitmap(e);
				getImage(id + 1);
			}
		);

Of course we have to register an Event for our loader, that will trigger when our image loading is done and we’re ready to process it!

As a callback we use an inline-function where we call our function generateBitmap to create a new Bitmap with smoothing turned on (I’ll explain this later).

and we’ll call our function getImage again with an incremented id

// creates a bitmap with smoothing turned on!
function generateBitmap(e):MovieClip {
	var tempBMP:Bitmap = new Bitmap(e.target.content.bitmapData, "auto", true);
	var tempMC:MovieClip = new MovieClip();
	tempMC.addChild(tempBMP);
	return tempMC;
}

Our generateBitmap function creates a new temporary Bitmap object and sets smoothing to true, which will give smoother results when tweening, scaling and moving our image!

In addition the function returns a MovieClip, that has the new Bitmap attached to it!

	//if all our images have finished loading
  } else {
		description.txtfield.text = "";
		next_btn.visible = prev_btn.visible = true;
		next_btn.addEventListener(MouseEvent.ROLL_OVER, highlightBtn);
		prev_btn.addEventListener(MouseEvent.ROLL_OVER, highlightBtn);
		next_btn.addEventListener(MouseEvent.ROLL_OUT, normalBtn);
		prev_btn.addEventListener(MouseEvent.ROLL_OUT, normalBtn);
		next_btn.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void{addImage(actualID + 1);} );
		prev_btn.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void{addImage(actualID - 1);} );

		imgHolder.buttonMode = true;
		imgHolder.addEventListener(MouseEvent.CLICK, openLink);

		//setting up the first image
		addImage(actualID);
	}
}

If our imageData returns null (which means, that every image has finished loading) we’ll be ready to start up our slideshow!

We just add some events to our controls that’ll highlight our buttons.

Note the Click events for our buttons, which will call our addImage function again with an incremented or decremented id!

function highlightBtn(e:MouseEvent):void {
	TweenLite.to(e.target, 0.25, {scaleX: 1, scaleY: 1, alpha: 1} );
}
function normalBtn(e:MouseEvent):void {
	TweenLite.to(e.target, 0.25, {scaleX: 0.8, scaleY: 0.8, alpha: 0.8} );
}

function showDescription(e:MouseEvent):void {
	TweenLite.to(description, fadeSpeed, {alpha: 1} );
}
function hideDescription(e:MouseEvent):void {
	TweenLite.to(description, fadeSpeed, {alpha: 0} );
}

Now these functions are very simple: we animate the buttons and our description a little bit with the TweenLite engine.

Every new Tween you register through TweenLite uses the function to, which needs 3 parameters:

  1. The object that will be tweened
  2. The Time (in seconds) that your tween will last
  3. an Object containing all the end values your tweened object should have at the end

The rest will be handled from TweenLite! Great, isn’t it?

For more information about how TweenLite works read the full documentation

function openLink(e:MouseEvent):void {
	navigateToURL(new URLRequest(imgData[actualID]["url"]), "_blank");
}

navigateToURL is a very simple function to call external URLs.
Just add a URLRequest containing your url-path as the first parameter
and optionally set the second parameter to _blank to open the link in a new window!

function addImage(id:Number):void {
	if (imgHolder.numChildren > 0) { //remove old image
		TweenLite.to(imgData[actualID]["image"], fadeSpeed, {alpha: 0, ease:Quint.easeIn, onComplete:
			function(){imgHolder.removeChildAt(0); addImage(id);}
		} );

Now here comes the fun part!

At first we check if there’s not already an image attached to our holder.
if so, we first fade it out with TweenLite.
Note that you can also add an easing to your tweens!

Most important here is the onComplete property.
We can define a function that will be called at the end of the tween.

In this case we use an inline function that removes our old image from our holder and
calls the addImage function again to finally add the new image!

	//if imgHolder has no image attached to it
	} else {
		actualID = id;
		if (actualID == 0) prev_btn.visible = false;
		else prev_btn.visible = true;
		if ((actualID + 1) == imgData.length) next_btn.visible = false;
		else next_btn.visible = true;

If our holder has no images attached to it, we can add our new one.

we set the actualID to the passed function id

and show or hide our buttons respectively to our id (meaning that for example if we reached the last image, we’d hide our next button and vice versa)

		description.txtfield.text = imgData[id]["description"]

		imgHolder.addChild(imgData[actualID]["image"]);
		imgData[id]["image"].alpha = 0;
		TweenLite.to(imgData[actualID]["image"], fadeSpeed, {alpha: 1, ease:Quint.easeOut} );
	}
}

Lastly we set the description to our textfield,
add the new image to our holder,
set its alpha to 0
and fade it in with TweenLite

 

 

Download

Download the full slideshow and all the project files for free!

 

Share

If you liked this tutorial, please share with a backlink to this post.
For questions or further feedback, feel free to comment!
Subscribe for updates and other tutorials

Read more from Flash / AS3
4 Comments Post a comment
  1. Sep 24 2010

    Thank you!
    Im learning how to use properly flash and XML, this was really useful, good job.

  2. David
    Oct 17 2010

    Very nice tutorial! THX a bunch!!

  3. Botrous
    Nov 9 2010

    Hi very nice tutorial, I had upload it to my web site but it didn’t load the photos just messag e loading 1/50 and black screen ….. i follow all the instruction and load every thing as you said but didn’t work can you help me to fix this problem …. Thanks

  4. nestor
    Feb 13 2011

    hi, very nice tutorial, i used it in my new virtual tour, really big help for me… Thank You

Share your thoughts, post a comment.

(required)
(required)

Note: HTML is allowed. Your email address will never be published.

Subscribe to comments