ActionScript 3.0 Image Galleries

While Colin Moock’s Essential ActionScript 3.0 provides a great foundation for understanding how ActionScript works, I find myself straying from the book to find solutions to common problems, such as how to build an image gallery. As I mentioned before, I was looking at developing an interface similar to the Album View used by Viewzi. Here’s what I’ve come up with so far:

The Box Class

First, I started by creating a Box class that I could use to build frames for the images. It’s pretty basic, providing parameters for width, height and color.

package {
	import flash.display.Shape;
	
	public class Box extends Shape {
		public function Box (w:Number,
                             h:Number,
                             fillColor:uint):void {
			graphics.beginFill(fillColor, 1);
			graphics.drawRect(0, 0, w, h);
		}
	}
}

The ImageFrame Class

Next, I created a class that creates an image frame with two instances of the Box class, one for the image placeholder, with parameters for width, height, color and margin, and one for the image frame, with parameters for color and padding.

package {
	import flash.display.*;
	
	public class ImageFrame extends Sprite {
		public function ImageFrame (imageWidth:uint, imageHeight:uint, imageColor:uint, frameColor:uint, padding:uint):void {
			var imageFrame:Sprite = new Sprite();

			// Create image placeholder
			var imageWidth:uint;
			var imageHeight:uint;
			var imageColor:uint;
			var image:Box = new Box(imageWidth, imageHeight, imageColor);

			// Create frame
			var padding:uint;
			var frameWidth:uint = imageWidth + (padding * 2);
			var frameHeight:uint = imageHeight + (padding * 2);
			var frameColor:uint = 0xFFFFFF;
			var frame:Box = new Box(frameWidth, frameHeight, frameColor);
			
			// Add frame and image to container
			imageFrame.addChild(frame);
			imageFrame.addChild(image);
			
			// Center image in frame
			image.x = padding;
			image.y = padding;
			
			// Add imageFrame
			addChild(imageFrame);
		}
	}
}

Floating Grid

These two classes then provided the basic building blocks to experiment with gallery configurations. The first attempt produced a grid of images that could be centered on the stage with a Tween effect added. To access the fl.transitions library, I copied the directory from the Flash CS3 ActionScript 3.0 classes built into the application. I found them here:

/Applications/Adobe Flash CS3/Configuration/ActionScript 3.0/Classes/fl/

I copied the classes into my development directory:

~/flex/data/interface/fl/

That way, I could access these classes using the Flex 3 SDK by importing the classes:


	import fl.transitions.*;
	import fl.transitions.easing.*;

To compile SWF files, I use this command in Terminal:

flex/bin/mxmlc flex/data/interface/FloatingGrid.as

I ran into some interesting problems created by attempting to add transitions to a scale effect on the mouse over state of each of the image frames. The Tween class appears to override the variables set to position the image frames in the container sprite. I may be able to solve the problem by setting a different registration point, something which doesn’t appear to exist in pure ActionScript 3.0 code, but can be created dynamically with a class such as the Dynamic MovieClip Registration class adapted by Oscar Trelles. At any rate, here’s what I came up with:

package {
	import flash.display.*;
	import flash.events.*;
	import flash.text.*;
	import fl.transitions.*;
	import fl.transitions.easing.*;
	
	public class FloatingGrid extends Sprite {
		// Instantiate stage variables
		public var sw:Number = stage.stageWidth;
		public var sh:Number = stage.stageHeight;
		public var bg:Shape = new Shape();

		// Instantiate container variables
		public var container:MovieClip = new MovieClip();
		public var containerX:Number;
		public var containerY:Number;
		public var containerWidth:Number;
		public var containerHeight:Number;

		// Instantiate image parameters
		public var imageNumber:uint = 36;
		public var imageWidth:uint = 40;
		public var imageHeight:uint = 40;
		public var margin:uint = 25;
		public var padding:uint = 4;
		public var imageColor:uint = 0x999999;
		public var frameColor:uint = 0xFFFFFF;
		
		// Set size on mouse over
		public var overScale:Number = 1.5;

		public function FloatingGrid () {
			// Turn scaling off and set alignment to top left
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			// Add a background color
			bg.graphics.beginFill(0x666666, 1);
			bg.graphics.drawRect(0,0,sw,sh);
			addChild(bg);
			
			// Add image frames to stage with the following parameters:
			// (number, width, height, margin, padding)
			imageFrames(imageNumber, imageWidth, imageHeight, imageColor, frameColor, margin, padding);

			// Register event listener for stage resizing
			stage.addEventListener(Event.RESIZE, resizeListener);

			// Register event listener for mouse over effects
			container.addEventListener(MouseEvent.MOUSE_OVER, over);
			container.addEventListener(MouseEvent.MOUSE_OUT, out);

			// apply tween
			var myTween:Tween = new Tween(container, "x", Elastic.easeOut, 0, containerX, 3, true);

		}

		private function resizeListener (e:Event):void {
			// Instantiate variables for current container position
			var initContainerX:Number = container.x;
			var initContainerY:Number = container.y;
			
			// Determine stage width and height
			sw = stage.stageWidth;
			sh = stage.stageHeight;
			
			// Set background to width and height of stage
			bg.width = sw;
			bg.height = sh;
			
			// Center the container on the Stage
			containerX = (sw / 2) - (containerWidth / 2);
			containerY = (sh / 2) - (containerHeight / 2);
			container.x = containerX;
			container.y = containerY;
			
			var containerTweenX:Tween = new Tween(container, "x", Elastic.easeOut, initContainerX, containerX, 3, true);
			var containerTweenY:Tween = new Tween(container, "y", Elastic.easeOut, initContainerY, containerY, 3, true);
		}

		private function over (e:MouseEvent):void {
			
			DisplayObject(e.target).scaleX = overScale;
			DisplayObject(e.target).scaleY = overScale;

			DisplayObject(e.target).x -= (imageWidth * (overScale - 1)) / 2;
			DisplayObject(e.target).y -= (imageHeight * (overScale - 1)) / 2;

			// var imageFreeTweenScaleX:Tween = new Tween(DisplayObject(e.target), "scaleX", Elastic.easeOut, 1, overScale, .5, true);
			// var imageFreeTweenScaleY:Tween = new Tween(DisplayObject(e.target), "scaleY", Elastic.easeOut, 1, overScale, .5, true);
			
			// var imageFreeTweenX:Tween = new Tween(DisplayObject(e.target), "x", Elastic.easeOut, 1, overScale, .5, true);
			// var imageFreeTweenY:Tween = new Tween(DisplayObject(e.target), "y", Elastic.easeOut, 1, overScale, .5, true);

		}

		private function out (e:MouseEvent):void {
			DisplayObject(e.target).scaleX = 1;
			DisplayObject(e.target).scaleY = 1;

			DisplayObject(e.target).x += (imageWidth * (overScale - 1)) / 2;
			DisplayObject(e.target).y += (imageHeight * (overScale - 1)) / 2;
		}

		private function imageFrames (
				imageNumber:uint,
				imageWidth:uint, 
				imageHeight:uint,
				imageColor:uint,
				frameColor:uint, 
				margin:uint, 
				padding:uint):void {
			// Instantiate image parameters
			var imageCount:uint = imageNumber;
			var imageX:int;
			var imageY:int;
			var imageOffset:Number = imageWidth + (padding * 2) + margin;
			var imageFrames:Array = new Array();

			// Calculate the columns and rows of images
			var columns:uint = Math.floor(sw / imageOffset);
			var rows:uint = Math.ceil(imageNumber / columns);

			// Number of images per row
			var imagesPerRow:uint = columns;
			
			// Calculate the width and height of the container
			containerWidth = columns * imageOffset - margin;
			containerHeight = rows * imageOffset - margin;
			
			// Create the container for the images
			addChild(container);
			
			// Center the container on the Stage
			containerX = (sw / 2) - (containerWidth / 2);
			containerY = (sh / 2) - (containerHeight / 2);
			container.x = containerX;
			container.y = containerY;
			
			// Add image frames to the container
			for (var row:int = 0; row < rows; row++) {
				imageX = 0;
				imageY = row * imageOffset;

				// Determine number of images for last row
				if (imageCount < imagesPerRow) {
					imagesPerRow = imageCount;
				}
				
				for (var i:int = 0; i < imagesPerRow; i++) {
					imageFrames[i] = new ImageFrame(imageWidth, imageHeight, imageColor, frameColor, padding);
					
					// Add images for the current row
					imageFrames[i].x = imageX + (i * imageOffset);
					imageFrames[i].y = imageY;
					container.addChild(imageFrames[i]);

					// Decrement images left to display
					imageCount--;
				}
			}
		}
	}
}

Gallery Slider

I never did find exactly what I was looking for in a gallery slider, although there are lots of examples out there. However, once I figured out that I shouldn’t be using a MouseEvent listener, but rather an Event.ENTER_FRAME listener and using the DisplayObject.mouseX variable, it all fell into place. By using some simple math, I could change the rate of acceleration based on how close the mouse position was to the left or right edges of the stage.

package {
	import flash.display.*;
	import flash.events.*;
	import flash.text.*;
	import fl.transitions.*;
	import fl.transitions.easing.*;
	
	public class GallerySlider extends Sprite {
		// Instantiate stage variables
		public var sw:Number = stage.stageWidth;
		public var sh:Number = stage.stageHeight;
		public var bg:Shape = new Shape();

		// Instantiate container variables
		public var container:MovieClip = new MovieClip();
		public var containerX:Number;
		public var containerY:Number;
		public var containerWidth:Number;
		public var containerHeight:Number;

		// Instantiate image parameters
		public var imageNumber:uint = 40;
		public var imageWidth:uint = 100;
		public var imageHeight:uint = 60;
		public var margin:uint = 25;
		public var padding:uint = 4;
		public var imageColor:uint = 0x999999;
		public var frameColor:uint = 0xFFFFFF;
		
		// Set size on mouse over
		public var overScale:Number = 1.25;

		public function GallerySlider () {
			// Turn scaling off and set alignment to top left
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			// Add a background color
			bg.graphics.beginFill(0x666666, 1);
			bg.graphics.drawRect(0,0,sw,sh);
			addChild(bg);
			
			// Add image frames to stage with the following parameters:
			// (number, width, height, margin, padding)
			imageFrames(imageNumber, imageWidth, imageHeight, imageColor, frameColor, margin, padding);

			// Register event listener for stage resizing
			stage.addEventListener(Event.RESIZE, resizeListener);

			// Register event listener for mouse position
			stage.addEventListener(Event.ENTER_FRAME, mousePositionListener);

			// Register event listener for mouse over effects
			container.addEventListener(MouseEvent.MOUSE_OVER, over);
			container.addEventListener(MouseEvent.MOUSE_OUT, out);

			// apply tween
			var myTween:Tween = new Tween(container, "x", Elastic.easeOut, 0, containerX, 3, true);

		}

		private function resizeListener (e:Event):void {
			// Instantiate variables for current container position
			var initContainerX:Number = container.x;
			var initContainerY:Number = container.y;
			
			// Determine stage width and height
			sw = stage.stageWidth;
			sh = stage.stageHeight;
			
			// Set background to width and height of stage
			bg.width = sw;
			bg.height = sh;
			
			// Center the container on the Stage
			containerX = (sw / 2) - (containerWidth / 2);
			containerY = (sh / 2) - (containerHeight / 2);
			container.x = containerX;
			container.y = containerY;
			
			var containerTweenX:Tween = new Tween(container, "x", Elastic.easeOut, initContainerX, containerX, 3, true);
		}

		private function mousePositionListener (e:Event):void {
			var stageCenter:Number = sw / 2;
			var quietZone:uint = 50;
			var stageLeft:Number = stageCenter - quietZone;
			var stageRight:Number = stageCenter + quietZone;
			var maxOffsetX:Number = stageCenter - imageWidth;

			// Move gallery left or right depending on mouse position
			// Movement accelerates as mouse gets close to left or right edges of the stage
			// A maximum offset value prevents the gallery from moving off the stage
			if (stage.mouseX < stageLeft && container.x  stageRight && (containerWidth + container.x > sw - maxOffsetX)) {
				container.x -= (stage.mouseX - stageRight) / 10;
			}
		}

		private function over (e:MouseEvent):void {
			
			DisplayObject(e.target).scaleX = overScale;
			DisplayObject(e.target).scaleY = overScale;

			DisplayObject(e.target).x -= (imageWidth * (overScale - 1)) / 2;
			DisplayObject(e.target).y -= (imageHeight * (overScale - 1)) / 2;

			// var imageFreeTweenScaleX:Tween = new Tween(DisplayObject(e.target), "scaleX", Elastic.easeOut, 1, overScale, .5, true);
			// var imageFreeTweenScaleY:Tween = new Tween(DisplayObject(e.target), "scaleY", Elastic.easeOut, 1, overScale, .5, true);
			
			// var imageFreeTweenX:Tween = new Tween(DisplayObject(e.target), "x", Elastic.easeOut, 1, overScale, .5, true);
			// var imageFreeTweenY:Tween = new Tween(DisplayObject(e.target), "y", Elastic.easeOut, 1, overScale, .5, true);

		}

		private function out (e:MouseEvent):void {
			DisplayObject(e.target).scaleX = 1;
			DisplayObject(e.target).scaleY = 1;

			DisplayObject(e.target).x += (imageWidth * (overScale - 1)) / 2;
			DisplayObject(e.target).y += (imageHeight * (overScale - 1)) / 2;
		}

		private function imageFrames (imageNumber:uint,
		                              imageWidth:uint, 
		                              imageHeight:uint,
		                              imageColor:uint,
		                              frameColor:uint, 
		                              margin:uint, 
		                              padding:uint):void {
			// Instantiate image parameters
			var imageX:int;
			var imageY:int;
			var imageOffset:Number = imageWidth + (padding * 2) + margin;
			var imageFrames:Array = new Array();

			// Calculate the width and height of the container
			containerWidth = imageNumber * imageOffset - margin;
			containerHeight = imageHeight;

			// Create the container for the images
			addChild(container);
			
			// Center the container on the Stage
			containerX = (sw / 2) - (containerWidth / 2);
			containerY = (sh / 2) - (containerHeight / 2);
			container.x = containerX;
			container.y = containerY;
			
			// Add image frames to the container
			for (var i:int = 0; i < imageNumber; i++) {
				imageFrames[i] = new ImageFrame(imageWidth, imageHeight, imageColor, frameColor, padding);
				
				// Add images for the current row
				imageFrames[i].x = imageX + (i * imageOffset);
				imageFrames[i].y = imageY;
				container.addChild(imageFrames[i]);
			}
		}
	}
}

Note: I updated the code by adding a maximum offset value to prevent the gallery from moving off the stage.

ActionScript 3.0 Galleries

There are some code examples available that I’ve been able to find so far:

Flash Galleries

Advertisements

About this entry