Title
Description
Body <p>While Colin Moock's <em>Essential ActionScript 3.0</em> 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 <a href="http://bauhouse.wordpress.com/2008/07/06/getting-started-with-actionscript-30/">mentioned before</a>, I was looking at developing an interface similar to the <a href="http://www.viewzi.com/search/therecordstore/">Album View</a> used by Viewzi. Here's what I've come up with so far:</p> <h3>The Box Class</h3> <p>First, I started by creating a <code>Box</code> class that I could use to build frames for the images. It's pretty basic, providing parameters for width, height and color.</p> <pre><code>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); } } }</code></pre> <h3>The ImageFrame Class</h3> <p>Next, I created a class that creates an image frame with two instances of the <code>Box</code> 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.</p> <pre><code>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); } } }</code></pre> <h3>Floating Grid</h3> <p>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 <code>fl.transitions</code> library, I copied the directory from the Flash CS3 ActionScript 3.0 classes built into the application. I found them here:</p> <pre><code>/Applications/Adobe Flash CS3/Configuration/ActionScript 3.0/Classes/fl/</code></pre> <p>I copied the classes into my development directory:</p> <pre><code>~/flex/data/interface/fl/</code></pre> <p>That way, I could access these classes using the Flex 3 SDK by importing the classes:</p> <pre><code> import fl.transitions.*; import fl.transitions.easing.*;</code></pre> <p>To compile SWF files, I use this command in Terminal:</p> <pre><code>flex/bin/mxmlc flex/data/interface/FloatingGrid.as</code></pre> <p>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 <a href="http://www.oscartrelles.com/archives/dynamic_movieclip_registration_with_as3">Dynamic MovieClip Registration</a> class adapted by Oscar Trelles. At any rate, here's what I came up with:</p> <pre><code>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--; } } } } }</code></pre> <h3>Gallery Slider</h3> <p>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 <code>MouseEvent</code> listener, but rather an <code>Event.ENTER_FRAME</code> listener and using the <code>DisplayObject.mouseX</code> 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.</p> <pre>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]); } } } }</pre> <p><strong>Note:</strong> I updated the code by adding a maximum offset value to prevent the gallery from moving off the stage.</p> <h3>ActionScript 3.0 Galleries</h3> <p>There are some code examples available that I've been able to find so far:</p> <ul> <li><a href="http://www.blog.noponies.com/archives/65">ActionScript 3 Full Browser XML Slideshow</a></li> <li><a href="http://www.blog.noponies.com/archives/89">Simple ActionScript 3 Slideshow Engine</a></li> <li><a href="http://www.blog.noponies.com/archives/61">ActionScript 3 Drag, Pan, Zoom XML Content</a></li> <li><a href="http://www.f-90.co.uk/blog/?p=7">Array based perpetual scrolling navigation AS3</a></li> </ul> <h3>Flash Galleries</h3> <ul> <li><a href="http://www.graphictherapy.com/index2.html">GraphicTherapy</a></li> <li><a href="http://www.noponies.com/dev/simplegallery/">ActionScript 3 Simple Gallery</a></li> <li><a href="http://www.noponies.com/dev/as3_xslideshow/">Simple ActionScript 3 SlideShow</a></li> <li><a href="http://www.noponies.com/dev/as3_slideimages/">ActionScript 3 Sliding Images XML Gallery</a></li> <li><a href="http://www.thereformation.co.nz/">The Reformation</a></li> <li><a href="http://www.noponies.com/dev/as3_pan_zoom/">ActionScript 3 Pan Zoom Drag Content</a></li> <li><a href="http://erikalmas.com/">Erik Almas Photography</a></li> <li><a href="http://joelhooks.com/2008/05/11/continuous-scrolling-image-thumbnail-and-slideshow-component-for-flex/">Continuous Scrolling Thumbnail Slideshow</a></li> <li><a href="http://2210media.com/xmlGallery/">XMLGallery</a></li> </ul>
Section Tutorials Contact Journal About Home
Category Applications Design History Ideas Technology
Tags
Date
DesignProjectX | The digital sandbox of Stephen Bau