Animating the images
Believe it or not, we’re almost there. The only major piece of functionality left to add is the actual animation of the images, and we already have almost everything in place. Add the code for the function:
private function animate(e:TimerEvent):void { }
In order for the animation to trigger, we’ll use the new Timer class, which replaces setInterval (and actually adds more control), so add the following in the onAddedToStage() function:
private function onAddedToStage(e:Event):void { ... [old code] ... animator = new Timer(ANIMATION_INTERVAL); animator.addEventListener(TimerEvent.TIMER, animate); animator.start(); }
and define
private const ANIMATION_INTERVAL:int = 50;
This way, the animate function will be called every 50 milliseconds.
Now, the animation will first have to follow some rules: – one image will be shown in the middle for X seconds; then the next item should slide in, but only if it has loaded; the animation speed should have some sort of “ease out” / braking effect.
Add this to the animate() function:
// don't do anything if there isn't at least one image if (loadedItem < 1) return; // check if there is a "next" image to display if (container.numChildren > displayedItems) { var picToShow:Bitmap = Bitmap(container.getChildAt(displayedItems)); var targetX:int = stage.stageWidth/2; var itemX:int = picToShow.x + picToShow.width/2 + container.x; var delta:int = (itemX-targetX) / ANIMATION_SPEED_FACTOR; } else { // the next image hasn't loaded yet, keep showing this one var picToShow:Bitmap = Bitmap(container.getChildAt(displayedItems-1)); var delta:int = 0; }
Don’t forget to define private var displayedItems:int; in your class.
The difference between loadedItems and container.numChildren is a little subtle. While the images are still loading, the two variables are equal; after the last image has loaded, the first and last image are duplicated, so the numChildren becomes loadedItems+2.
We use container.numChildren to determine how many images are available. The idea is that if 5 images have loaded, and currently in the middle is image 3, we can animate the images container to reveal the next one.
For this, I’m using a very simplistic method: We calculate the difference between the center of the Stage (stage.stageWidth/2) and the center of the next image to show in full (picToShow.x + picToShow.width/2 + container.x) on X axis (width). With each iteration, the container is supposed to travel a fraction of that distance. Suppose the initial difference is 100 pixels; if the cut factor is 2, at the first iteration the container will be moved to the left 50 pixels (100/2), then 25, 13, 7, 4, 2, 1, 0 (delta is an integer, so it takes no fractional values). This effectively gradually slows down the animation. For practical purposes I’ve found
private const ANIMATION_SPEED_FACTOR:int = 6;
to work best in my case.
This whole approach works well only if the Slideshow displays 2-3 images at any time (one fully visible in the middle and two partially visible on its left and right). If the images are not wide enough, some white space will be visible on the right side when the animation reaches the end. If you intend to make a gallery that displays any number of images at the same time, you’ll have to get rid of the whole “image in center” concept and make the delta calculations based on the left/right edge.
Next, let’s handle the (easy) situation when delta is 0, meaning that the image is stopped. We have two possibilities there – either the image has just stopped after an animation, in which case we should display the slide title, or that the current image has been displayed long enough and it’s time to show the next one.
For checking the time elapsed, we could use the Timer class, but I prefer an old friend: getTimer(). To use it, you must first import the utils package.
import flash.utils.*;
Note: if you’re unfamiliar with getTimer(), it returns the number of milliseconds since the flash movie has started playing. The difference between two getTimer() values at different points in time gives us the time elapsed in between calls. Because of that, we must define another variable to store the previous reading:
private var lastTime:int;
I’ll also declare
private const ANIMATION_STILL_TIME:int = 4;
as the number of seconds an image should stay still before the next one slides in.
This code gets added to animate():
if (delta <= 0) { // the next image should be displayed... if (((getTimer()-lastTime) >= ANIMATION_STILL_TIME * 1000) && (loadedItems > displayedItems)) { lastTime = getTimer(); displayedItems++; titleField.text = ""; } else { // we should display the title for the current image if (titleField.text == "" && titles[container.getChildIndex(picToShow)] != null) titleField.text = titles[container.getChildIndex(picToShow)]; } }
Piece of cake!
Last thing to do is to handle the actual motion.
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 | private function animate(e:TimerEvent):void { // don't do anything if there isn't at least one image if (loadedItems < 1) return; // check if there is a "next" image to display if (container.numChildren > displayedItems) { var picToShow:Bitmap = Bitmap(container.getChildAt(displayedItems)); var targetX:int = stage.stageWidth/2; var itemX:int = picToShow.x + picToShow.width/2 + container.x; var delta:int = (itemX-targetX) / ANIMATION_SPEED_FACTOR; } else { // the next image hasn't loaded yet, keep showing this one var picToShow:Bitmap = Bitmap(container.getChildAt(displayedItems-1)); var delta:int = 0; } // the image has stopped... if (delta <=0) { // the next image should be displayed... if (((getTimer()-lastTime) >= ANIMATION_STILL_TIME * 1000) && (loadedItems >= displayedItems)) { lastTime = getTimer(); displayedItems++; titleField.text = ""; } else { // we should display the title for the current image if (titleField.text == "" && titles[container.getChildIndex(picToShow)] != null) titleField.text = titles[container.getChildIndex(picToShow)]; } } // the image is still moving else { // check if we need to wrap around // conditions: we've already displayed all loaded items and the last image is now just at the left edge if ((displayedItems==loadedItems) && ((container.x + container.getChildAt(displayedItems-1).x) <= 0)) { var diff:int = 0 - (container.x + container.getChildAt(displayedItems-1).x); // seamlessly move the container back at the beginning and reset displayed images container.x = container.getChildAt(container.numChildren-1).width - diff; displayedItems = 0; } // move the container to the left container.x -= delta; } } |
The code should be pretty easy to follow, especially if you read the comments. The only tricky part is the wrap-around. The condition for wrap-around is when the last loaded image was in center and its left edge has just come off the left side of the Stage. To make it easier to follow, I made this diagram.

The condition (container.x + container.getChildAt(displayedItems-1).x) <= 0) becomes clear. When displayedItems==loadedItems, displayeditems-1 is the same as loadedItems-1, the last image loaded (newbies only: remember that in flash, all indexes are zero-based, if there are n elements, they lay within the interval [0..n-1]). The x position of this last image relative to its parent (container) is always the same, but the x position of the container itself changes, so we calculate the x position of image relative to the Stage. When this position becomes zero or less, we move the container back at the beginning, taking in consideration the width of the duplicated image. container.getChildAt(container.numChildren-1) returns a reference to the last image in container, which is not the last loaded image, but the last duplicated image. The x position of this duplicated image is negative (the first loaded image starts at x=0) so that's why the container.x has to be adjusted to its width.
On the next page, we'll apply some finishing touches
I’m having a serious problem that’s just recently appeared. my flash cs3 is unable to build a classpath to the external actionscript in your tutorial example as well as others I’ve downloaded tonight. it’s driving me crazy. have you ever encountered this?
my program was corrupted. had to re-install from scratch. fine now!
thanks for this terrific example….
This was dead on and will really help out. Thanks for the time and effort to detail the code. Mahalo!
Spot on. Very useful. Thanks
This is great I’m new to actionscript and this was very helpful. It looks really nice. I was wondering, how do i go about making the images ‘clickable’?
Thanks
Excellent presentation Armand. I
Awesome flash based slide show, great work explaining it step by step as well. Thanks for sharing, keep up the good work.
Thank you very much for the tutorial, especially the pdf and full code. I linked you to where I will use it and cite for credit. Thanks!
Excellent tutorial, Armand. I will share this link with my design students. Thanks much.
This is the best tutorial/example I’ve found on the subject.
One thing you could do different is to keep the image source and title strings directly in the XML object, since it is already loaded in memory, no reason to create another copy of it in memory. A slideshow is a linear arrangement but frequently the user can jump around so it isn’t necessary to have things in linear order. You end up using the id names. If you have a large number of images, say your slideshow totals into the 100’s of megs, it may be better to load them on demand, not all at once.