Tutorial: Create an XML Driven, Cross-fading Slideshow

June 05, 2007

Note: For simplicity's sake, this tutorial uses the excellent XML2Object Class written and distributed by Alessandro Crugnola from Sephiroth.it. Additionally this tutorial makes use of mx.transitions.Tween which are included with flash.

This tutorial will teach you how to create a simple timed, cross-fading photo album. The basic principles here can be used to expand this project into something far more impressive, but my goal is just to introduce some of the concepts and leverage a bit of their power.

To begin this tutorial you'll need to create a new flash document sized to 300px x 250px so the example images provided to you fill the screen.

Open up the Actions panel. Since most of you are beginner to intermediate flash users, lets discuss best practices for developing an actionscript file or frame. Using proper formation or even a structured formation to your actionscript will make your and peoples you work with lives much easier. Using a uniform structure helps when you need to make edits or when you want to re-use code (which should be often).

I like to structure my actionscript into three main sections: import and include statements, variables and properties, and functions. Each section can be further split up in to subsections, when you get into creating custom classes, but this should be fine for our purposes.

Place the following code inside your first frame to denote the sections we'll be working with:

/****************************************************************/
// EXTERNALS
/****************************************************************/

/****************************************************************/
// VARIABLES
/****************************************************************/

/****************************************************************/
// FUNCTIONS
/****************************************************************/

For this project I also recommend setting apart a special section for dealing with the XML:

/****************************************************************/
//  XML HANDLING
/****************************************************************/

Lets use a very basic XML file (images.xml) to define where our images live. For this tutorial, I've decided to keep everything within the same directory, but this could work just as well with the images at a different path, just add the path to the XML. Remember to form your XML well and include a container tag. Here, mine is .



	image1.jpg
	image2.jpg
	image3.jpg
	image4.jpg

Now to get the XML into the flash create an XML object and a variable that defines the location of your XML file, then load your XML:

/****************************************************************/
// VARIABLES
/****************************************************************/

var xmlFile:String = "images.xml";
/****************************************************************/
// XML HANDLING
/****************************************************************/

myXML = new XML();
myXML.load(xmlFile);

Additionally, we want to do something afther the XML is fully loaded so define an onLoad Event Handler for myXML

myXML.onLoad = function(){
	trace("XML loaded");
}

Once the XML is loaded we want to use XML2Object to parse the XML data into a serial object that is easier to deal with. Add the following code to import the XML2Object Functionality to your Movie:

/****************************************************************/
// EXTERNALS
/****************************************************************/

import it.sephiroth.XML2Object; 

Create an object where we can place our serialized XML data and place it in the Variables Section, And within the onLoad event handler we just created , add the code to make XML2Object work it's Magic.

/****************************************************************/
// VARIABLES 
/****************************************************************/

var xmlFile:String = "images.xml";
var serialObject:Object = new Object();
/****************************************************************/
// XML HANDLING
/****************************************************************/

myXML = new XML();
myXML.load(xmlFile);
myXML.onLoad = function(){
	trace("XML loaded");
	var xmlParser:XML2Object = new XML2Object();
	serialObject = xmlParser.parseXML(this);
}

The serialObject will contain all the data from the XML file, reachable via dot syntax. Where ther eare multiple nodes with the same name, there will be an array with the name of the nodes, indexed from 0.

Now that we've got all our data in place, it's time to do something with it, Namely, load some images. Create a loadImage() function. With this function we want to load an image into a placeholder MovieClip so we can take advantage of all the cool methods and properties MovieClips have. Since the image will effectively be a child of our placeholder, it will have inherit all the properties of it's host. We're going to concentrate on the _alpha property, but more on that later. First lets load an image into our movie:

/****************************************************************/
// FUNCTIONS
/****************************************************************/

function loadImage(f_imageURL:String):Void{
	
	var myMCL:MovieClipLoader = new MovieClipLoader();
	createEmptyMovieClip("imageHolder", getNextHighestDepth());
	myMCL.loadClip(f_imageURL, this.imageHolder);
}

Here I've defined a function that will load create an empty MovieClip called imageHolder and loads an image who's location is passed to it when called. This is the meat of the process in its simplest form. It doesn't return a value when called so it's data type is set to Void. I promised some crossfading so lets fancy this up a bit.

We are going to be loading multiple images and to make garbage collection a bit easier, lets setup an index value that keeps track of where we're at in the process of things.

/****************************************************************/
// VARIABLES 
/****************************************************************/

var xmlFile:String = "images.xml";
var serialObject:Object = new Object();
var imageIndex:Number = 0;

Since we're setting up a cross fade we need to make our load script a bit more dynamic. The way it is, each image will automatically replace then last when it loads in so each image needs it's own unique container. To do this we pass the index value to loadImage() and create new imageHolders based on that value.

/****************************************************************/
// FUNCTIONS
/****************************************************************/

function loadImage(f_imageURL:String, f_index:Number):Void{
	
	var myMCL:MovieClipLoader = new MovieClipLoader();
	
	createEmptyMovieClip("imageHolder" + f_index, getNextHighestDepth());
	myMCL.loadClip(f_imageURL, this["imageHolder" + f_index]);
}

the this[] syntax let us create new variables and objects on-the-fly without knowing the specific name of the variable or object , but rather setting up a naming convention.

Next we need to create a way to quickly and easily load images in sequence so we create a new function nextImage().

/****************************************************************/

function nextImage():Void{
	loadImage(serialObject.contents.image[imageIndex].data, imageIndex);
	imageIndex++;
}

/****************************************************************/

nextImage() calls loadImage and passes it a value from the searialObject we created from XML using the imageIndex to find the correct image in sequence. It also passes imageIndex along to LoadImage to be used as a unique ID when creating new imageHolder clips. After it starts the image loading process, it increments the imageIndex so next time nextImage() is called it will provide data for the next image in the sequence.

Now to add the fading part. For this we'll use the Tween class that ships with flash. To do so, we need to add a couple lines to the externals section of the script.

/****************************************************************/
// EXTERNALS
/****************************************************************/

import it.sephiroth.XML2Object; 
import mx.transitions.Tween;
import mx.transitions.easing.Regular;

These two lines setup the use of the tween class for use as our fading mechanizm.

In order to get the fades to work in sequence though, we need to add an event listener to the MovieClipLoader we created in loadImage() and give it an event handler that is triggered once the image is fully loaded in our movie.

/****************************************************************/
// FUNCTIONS
/****************************************************************/

function loadImage(f_imageURL:String, f_index:Number):Void{
	
	var myMCL:MovieClipLoader = new MovieClipLoader();
	var MCLListener:Object = new Object();
	myMCL.addListener(MCLListener);
	
	MCLListener.onLoadInit = function(targetMC){
	
	}
	
	createEmptyMovieClip("imageHolder" + f_index, getNextHighestDepth());
	myMCL.loadClip(f_imageURL, this["imageHolder" + f_index]);
}

Now to get the loaded inage to fade up from nothing over the top of the previous image, we need to set it's imageHolder's initial _alpha to 0, then use the Tween object to gradually bring it up to 100 after it has fully loaded. So set the created movieClip's alpha to 0 then within the onLoadInit event handler create the tween object.

When creating a new tween Object you must pass it a lot of information to make it work. The Tween object syntax is as follows: new Tween(Object, “_propertyToTween”, method, startValue, endValue, duration, isDurationInSeconds);

We can also create an event handler onMotionFinished to execute once the tween reaches it's end value. Here, we'll use it for some garbage collection to keep the elements on screen to a minimum. After an image is faded up completely, we'll remove the image behind it using a new function removeLastImage().

/****************************************************************/
// FUNCTIONS
/****************************************************************/

function loadImage(f_imageURL:String, f_index:Number):Void{
	
	var myMCL:MovieClipLoader = new MovieClipLoader();
	var MCLListener:Object = new Object();
	myMCL.addListener(MCLListener);
	
	MCLListener.onLoadInit = function(targetMC){
		var alphaTween:Tween = new Tween(targetMC, "_alpha", Regular.easeOut, 0, 100, 1, true);
		alphaTween.onMotionFinished = function(){
			removeLastImage();
		}
	}
	
	createEmptyMovieClip("imageHolder" + f_index, getNextHighestDepth());
	this["imageHolder" + f_index]._alpha = 0;
	myMCL.loadClip(f_imageURL, this["imageHolder" + f_index]);
}

We want to actually look at each image we need to create a timer that will wait a specified amount of time before moving on to the next image. We do this with setinterval. Since setinterval will continue to run at the devined amount of time over and over again we need to clear the last interval before creating a new one. So, our Final loadImage() function looks like:

/****************************************************************/
// FUNCTIONS
/****************************************************************/

function loadImage(f_imageURL:String, f_index:Number):Void{
	
	var myMCL:MovieClipLoader = new MovieClipLoader();
	var MCLListener:Object = new Object();
	myMCL.addListener(MCLListener);
	
	MCLListener.onLoadInit = function(targetMC){
		var alphaTween:Tween = new Tween(targetMC, "_alpha", Regular.easeOut, 0, 100, 1, true);
		alphaTween.onMotionFinished = function(){
			removeLastImage();
		}
		clearInterval(_root.imageTimer);
		_root.imageTimer = setInterval(nextImage, 3*1000);
	}
	
	createEmptyMovieClip("imageHolder" + f_index, getNextHighestDepth());
	this["imageHolder" + f_index]._alpha = 0;
	myMCL.loadClip(f_imageURL, this["imageHolder" + f_index]);
}

Since nextImage() moves on to the next index when it loads the current image we need to remove the image 2 index units back, removing the image one index unit back would be removing the current image. So the removeLastImage function is:

/****************************************************************/

function removeLastImage():Void{
	this["imageHolder" + (imageIndex - 2)].removeMovieClip();
}

/****************************************************************/

Finally to make sure the whole sequence gets started we need to add a call to get nextImage() just after the XML data has been loaded and parsed:

/****************************************************************/
//  XML HANDLING
/****************************************************************/

myXML  = new XML();
myXML.load(xmlFile);
myXML.onLoad  = function(){
    trace("XML  loaded");
    var  xmlParser:XML2Object = new XML2Object();
    serialObject  = xmlParser.parseXML(this);
    nextImage();
}

Put it all together and you get a sequential, cross-fading, XML driven slideshow.

Download the source files

Patrick Gunderson is designer and developer working at NFL.com in Los Angeles.