Simple AS3 Slideshow Widget: Part 3

This is the final post in a three part tutorial detailing the creation of a Flash slideshow widget using ActionScript 3.0. Have a look over Part 1 and Part 2 before moving ahead.

In this post, we are examining the two methods which actually handle image loading and display within our module.

1
2
3
4
5
6
7
8
9
10
11
12
private function switchImage(e:TimerEvent):void {
	imageBitmapData.draw(this);
	imageBitmap.bitmapData = imageBitmapData;
	imageBitmap.alpha = 1;
	if(currentImage < imageArray.length-1){
		currentImage++;
	}else{
		currentImage = 0;
	}
	urlRequ.url = appPath + "path/to/images/" + imageArray[currentImage];
	imageLoader.load(urlRequ);
}

“switchImage” is invoked by the Timer we established previously. The function this method is to set up our eventual transition from one image to the next, grab the next image from our Array and begin to load it into our Loader instance on the Stage, and keep track of what position we are at in our Array. To accomplish this, we first “draw()” the entire Stage to our “imageBitmapData” instance. This data is then assigned to the “imageBitmap” bitmapData property, effectively duplicating the visible data within our Loader instance. This switch from Loader to Bitmap is in no way apparent to the viewer. We also switch the alpha property of out Bitmap to 1, ensuring that it is completely visible.

We then manage our “currentPosition” variable by checking the current value against the Length of the Array. If we have reached the end, we simply revert to a value of “0” causing an infinite loop.

All we have to do then is change the “url” property of our URLRequest object and then invoke another load() upon “imageLoader”.

1
2
3
private function imageLoaded(e:Event):void {
	imageTween = new Tween(imageBitmap, "alpha", Regular.easeIn, 1, 0, 2, true)
}

This is the last method we have in our little widget. “imageLoaded” is fired by an Event.COMPLETE when a loading image is finally loaded up. At this point, we simply use Tweener to tween the alpha of our Bitmap (displaying the previous image) down to 0, revealing the freshly loaded image beneath.

I recommend using SWFObject to embed the module into your website.

Here is the full PHP code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php 
$firstrun = true;
$files = "";
$dir = opendir("front_images");
while (false !== ($file = readdir($dir))) {
	if (strpos($file, ".gif", 1) || strpos($file, ".jpg", 1) || strpos($file, ".png", 1)) {
		if($firstrun){
			$files = $files . "" . $file;
			$firstrun = false;
		}else{
			$files = $files . "," . $file;
		}
	}
}
echo $files;
?>

Here is the full ActionScript class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package {
	import flash.display.Sprite;
	import flash.display.Loader;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	import fl.transitions.Tween;
	import fl.transitions.easing.*;
 
	public class SlideShow extends Sprite {
		private var appPath:String;
		private var imgDur:int;
		private var currentImage:int;
		private var urlRequ:URLRequest;
		private var urlLoad:URLLoader;
		private var imageArray:Array;
		private var imageBitmap:Bitmap;
		private var imageBitmapData:BitmapData;
		private var imageLoader:Loader;
		private var imageTimer:Timer;
		private var imageTween:Tween;
 
		public function SlideShow():void {
			if (root.loaderInfo.parameters.appPath != undefined) {
				appPath = root.loaderInfo.parameters.appPath;
			} else {
				appPath = "http://yourwebsite.com/";
			}
			if (root.loaderInfo.parameters.imgDur != undefined) {
				imgDur = root.loaderInfo.parameters.imgDur*1000;;
			} else {
				imgDur = 5*1000;
			}
			gatherFiles();
		}
 
		private function gatherFiles():void {
			urlRequ = new URLRequest();
			urlRequ.url = appPath + "gatherFrontImages.php";
			urlLoad = new URLLoader();
			urlLoad.dataFormat = flash.net.URLLoaderDataFormat.TEXT;
			urlLoad.addEventListener(Event.COMPLETE, urlComplete);
			urlLoad.load(urlRequ);
		}
 
		private function urlComplete(e:Event):void {
			imageArray = e.target.data.split(",");
			currentImage = imageArray.length-1;
			imageLoader = new Loader();
			imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded);
			this.addChild(imageLoader);
			imageBitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
			imageBitmap = new Bitmap();
			this.addChild(imageBitmap);
			switchImage(new TimerEvent(TimerEvent.TIMER));
			imageTimer = new Timer(imgDur);
			imageTimer.addEventListener(TimerEvent.TIMER, switchImage);
			imageTimer.start();
		}
 
		private function switchImage(e:TimerEvent):void {
			imageBitmapData.draw(this);
			imageBitmap.bitmapData = imageBitmapData;
			imageBitmap.alpha = 1;
			if(currentImage < imageArray.length-1){
				currentImage++;
			}else{
				currentImage = 0;
			}
			urlRequ.url = appPath + "path/to/images/" + imageArray[currentImage];
			imageLoader.load(urlRequ);
		}
 
		private function imageLoaded(e:Event):void {
			imageTween = new Tween(imageBitmap, "alpha", Regular.easeIn, 1, 0, 2, true)
		}
 
	}
}

Simple AS3 Slideshow Widget: Part 2

In Part 1, we had a look at the basic structure of what we are working toward, including a PHP file to read images from a directory and return a list of images to Flash, the setup of our FLA, and the basic construction of our document class. This widget, being so simple, will only require the document class.

We will now go through each method and describe what is happening…

1
2
3
4
5
6
7
8
9
10
11
12
13
public function SlideShow():void {
	if (root.loaderInfo.parameters.appPath != undefined) {
		appPath = root.loaderInfo.parameters.appPath;
	} else {
		appPath = "http://yourwebsite.com/";
	}
	if (root.loaderInfo.parameters.imgDur != undefined) {
		imgDur = root.loaderInfo.parameters.imgDur*1000;;
	} else {
		imgDur = 5*1000;
	}
	gatherFiles();
}

There are a few things to set up in our constructor function. These values can be passed in through flashvars or hard-coded into your class. We are being super flexible and allowing the user to input these values upon embed. For a short explanation of what is going on here, have a look at my previous post.

We now assign values to the “appPath” and “imgDur” variables. “appPath” is simply the path to our actual application or website. We will use this to construct both calls to the PHP file and also the images themselves. “imgDur” is a setting that specifies how long each image should persist on the screen before fetching another. We then call “gatherFiles()” after these values are established.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package {
	import flash.display.Sprite;
	import flash.display.Loader;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	import fl.transitions.Tween;
	import fl.transitions.easing.*;
 
	public class SlideShow extends Sprite {
		private var appPath:String;
		private var imgDur:int;
		private var currentImage:int;
		private var urlRequ:URLRequest;
		private var urlLoad:URLLoader;
		private var imageArray:Array;
		private var imageBitmap:Bitmap;
		private var imageBitmapData:BitmapData;
		private var imageLoader:Loader;
		private var imageTimer:Timer;
		private var imageTween:Tween;

As we step through this and encounter new variables, they will of course need to be defined and datatyped properly at the beginning of our class. We will also need to import any other classes we need within our package. The full list of import statements and variable declarations is included here for the sake of overview.

An important thing to note here, is that we are importing classes that are native to Flash, but are also importing the excellent “Tweener” packages which can be found at Google Code. You could use the build in tween classes, but Tweener is a lot more flexible and provides much more control.

Now, on to our data loading methods!

1
2
3
4
5
6
7
8
private function gatherFiles():void {
	urlRequ = new URLRequest();
	urlRequ.url = appPath + "gatherFrontImages.php";
	urlLoad = new URLLoader();
	urlLoad.dataFormat = flash.net.URLLoaderDataFormat.TEXT;
	urlLoad.addEventListener(Event.COMPLETE, urlComplete);
	urlLoad.load(urlRequ);
}

Our goal with this method is to create a connection to the PHP file and establish a handler for the results. In order to do this, we will use the “URLLoader” class. URLLoader has a method called “load” which requires a “URLRequest” object. URLRequest has a “url” property which we set using a combination of the “appPath” variable defined upon initialization and the location and name of our PHP file. We also set the “dataFormat” property of the URLLoader to “flash.net.URLLoaderDataFormat.TEXT”. This will ensure that the data we receive is properly interpreted as text. Finally, we add an event listener to watch for an “Event.COMPLETE” to our URLLoader and invoke the “load()” method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private function urlComplete(e:Event):void {
	imageArray = e.target.data.split(",");
	currentImage = imageArray.length-1;
	imageLoader = new Loader();
	imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded);
	this.addChild(imageLoader);
	imageBitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
	imageBitmap = new Bitmap();
	this.addChild(imageBitmap);
	switchImage(new TimerEvent(TimerEvent.TIMER));
	imageTimer = new Timer(imgDur);
	imageTimer.addEventListener(TimerEvent.TIMER, switchImage);
	imageTimer.start();
}

“urlComplete” is the method that fires off once our URLLoader request is returned to Flash. We are doing four main things here for the final setup of our slideshow.

  1. First, we use the “split()” method to assign each file name to an Array called “imageArray”. We access the returned data (comma-delimited string) through the data property of the event target. The “currentImage” variable keeps track of which image is being displayed later on. We now set this to the Array length minus one. This provides us with the final index of our Array.
  2. Next, we will set up our display objects and add them to the display list. We create a Loader instance “imageLoader” to hold our loaded image and display it upon the Stage. We also add an event listener to the “contentLoaderInfo” property of our Loader instance. This will enable us to detect when the load has completed so that we can safely display the next image.
  3. Similar to the Loader instance, we now create a new BitmapData object “imageBitmapData” matching the size of our Stage. This will be used later on during image transitions and will feed a Bitmap object “imageBitmap” that we now create and add to the Stage.
  4. Finally, we set up a Timer to run based off of the “imgDur” variable set upon initialization. We just need to add an event listener to the Timer to handle each new image load and then invoke the “start()” method to get things rolling. Note that I am also manually triggering the “switchImage” method so we do not have to wait a full tick before our first image is shown.

Now we have our data imported and arranged nicely. Our structures and display list are all established. Everything is set up to begin actually displaying these images upon the Stage. In Part 3, we will examine the two remaining methods and have a look at the full, completed class structure.

Simple AS3 Slideshow Widget: Part 1

This is a simple example of how to build a dynamic slideshow widget in Flash using ActionScript 3.0 such as the one seen here.

The first thing you’ll need to do is set up an FLA with the properties you’d like to exhibit. Stage resolution is set to 500×300 with a black background. This will vary depending on the size of your generated images. Be sure that your FLA is set to use ActionScript 3.0 and declare your document class, in this case “SlideShow”.

Let’s set up a PHP file to read from the directory of our choosing and return a list of images to us.

1
2
3
$firstrun = true;
$files = "";
$dir = opendir("images");

So, here we declare some variables. “$firstrun” to determine where we are in our loop, and “$files” to hold the comma-delimited list the loop will produce. “$dir” is a directory reference for our image directory.

1
2
3
4
5
6
7
8
9
10
11
while (false !== ($file = readdir($dir))) {
	if (strpos($file, ".gif", 1) || strpos($file, ".jpg", 1) || strpos($file, ".png", 1)) {
		if($firstrun){
			$files = $files . "" . $file;
			$firstrun = false;
		}else{
			$files = $files . "," . $file;
		}
	}
}
echo $files;

The next part is pretty simple as well. We create “$files” and populate it with all of the files in our directory. Next, we simply loop over each item and look for certain character sequences to determine whether we should include a particular file in our list of images. You can see that the “$firstrun” Boolean is used here to determine comma placement for each item.

It is assumed at this point that we have also generated a set of images to use in our slide show and have dumped them into our “images” directory on the server.

So now it’s time to create an ActionScript class to use as the FLA document class. We called this class “SlideShow” in our FLA so this is the exact name we will need to give the file “SlideShow.as”.

1
2
3
4
5
6
package {
	import flash.display.Sprite;
	public class SlideShow extends Sprite {
		public function SlideShow():void {}
	}
}

Here is the basic setup for our SlideShow class. It extends flash.display.Sprite so we have to be sure to import that in our package. Planning our class, we are going to need to define a few different methods:

gatherFiles()
This will be used to create a connection to the server and run the PHP code we previously created.

urlComplete()
Will handle the processing of the comma-delimited String which is returned by PHP and the setup of our display objects on the Stage.

switchImage()
Will handle the image management and loading features.

imageLoaded()
This will serve to process our image transitions and is triggered as each image load completes.

In Part 2, we will look at each of these methods in detail.

Aptana, Eclipse Europa, and AIR

I fist tried Eclipse as a web development IDE a little over one year ago. At the time, I had mostly just stuck with Dreamweaver for all of my general web/PHP/ColdFusion needs. I had heard that the new version of Flex Builder was being built on Eclipse and figured to give it a shot.

I was severely disappointed initially- the IDE was slow, clunky, and configuration was a nightmare. Performing a quick uninstall and returning to Dreamweaver took all of 30 minutes. I tried installing again from time to time, but things were always the same. When JRE 6 was released, this did speed things up quite a bit and at that time I also discovered the Aptana project. Having a “web IDE” install like Aptana was pretty convenient, but I still remained solid with my regular development platform.

With the release of Flex Builder 2, I couldn’t help but use Eclipse more often for MXML editing and so became more familiar with it as a development IDE. I was still annoyed by some small things like no text drag and drop… file not associated with a project could not be opened… line numbering randomly turning itself off… but things were generally better the more experience gathered.

With the release of Eclipse 3.3 [Europa], I have to say that a lot of my misgivings have been taken care of. Not only can you now drag and drop text from line-to-line and edit files outside of the “Project” paradigm, but it is much more responsive as well. With an update of Aptana I notice that it now has an AIR plug to compile and deploy AIR apps built with HTML/JavaScript. I cannot help but wonder what a killer environment Aptana would be with MXML/AS3 support! That would be a serious development platform!

Google Gears and Apollo… and Tubes?

Since reading this announcement on the Google Gears API Blog last evening, I’ve been wondering to myself (from time-to-time) how this would/could/will apply to the Apollo runtime. Apollo’s integrated web engine is WebKit which is the same engine the Safari browser runs on… but Safari is not supported by the Gears API as of yet. Though as stated on the Gears site- it will be.

It is mentioned in that same announcement that Adobe is one of the “industry partners” working with Google on this project. I can only imagine that there will be some base shared technology between Google Gears and the Apollo local database probably in the form of SQLite?

There are three features touted on the Google Gears website that are of interest:

  1. Store and serve application resources locally
  2. Store data locally in a fully-searchable relational database
  3. Run asynchronous Javascript to improve application responsiveness

Point #1 can already be achieved through the Apollo alpha using the integrated File API and point #3, I believe, is possible as well since Apollo apps can be built with AJAX (though I have only dealt with Apollo through Flex and cannot confirm anything specific on that front). So that leaves point #2… this capability within Apollo would be amazing!


“It’s not a big truck. It’s a series of tubes!”