Curved 3D Wall in Flash Player 10

While catching up on various blog posts earlier today, I ran across this very nice example of a new Flash component which utilizes Papervision3D. As wonderful as it is, my first thought was, hey, you could do something like that natively in Flash 10 – which is exactly what I sat down and did.

Now, my example is not nearly as lovely as the component example and a good deal of the code I’m about to share is chocked full hard coded kludge, but it was kind of a rush job (’bout an hour’s worth of work) and was really just me seeing if it could be done. There’s a chance I’ll clean this up and redistribute at a later date – maybe a future component of my own. Until then however, here’s something to play around with, if anyone’s interested. Bear in mind, this would just as easily support video files – I’m just trying save a little bandwidth:

First, here is a small portion of the .xml being loaded, just to give the idea (yes, these are flicks from my personal stash):

EDIT: Seems after updating wordpress, my code highlighter will no longer support xml and wordpress throws in random tags here and there. Thanks WP..
The xml only contains a root node and several “movie” nodes. Each movie node contains an “image” node (a path to the .jpg file) and a “link” node (a path to an Amazon page). And that’s it…

The following is the class used for the thumbnail images. It basically just sticks an image inside a smoothed bitmap instance, resizes it to a standard size, stores a link URL, and adds some nice little roll over/out effects with the help of Tweener:

package { import caurina.transitions.Tweener; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.display.Sprite; import flash.events.MouseEvent; import flash.filters.GlowFilter; import flash.geom.Matrix; import flash.net.navigateToURL; import flash.net.URLRequest; public class ThumbNail extends Sprite { private var _imageWidth:int; private var _imageHeight:int; private var _link:String; public function ThumbNail(w:int, h:int, image:DisplayObject, link:String = null):void { _imageWidth = w; _imageHeight = h; _link = link; createThumb(image); } private function createThumb(img:DisplayObject):void { var bmd:BitmapData = new BitmapData(img.width, img.height); bmd.draw(img); img = null; var bmp:Bitmap = new Bitmap(bmd, "auto", true); bmp.width = _imageWidth; bmp.height = _imageHeight; var s:Sprite = new Sprite(); s.addChild(bmp); s.mouseChildren = false; s.x -= bmp.width * .5; s.y -= bmp.height * .5; s.filters = [new GlowFilter(0x000000, .75, 0, 0, 16, 1, true)]; s.addEventListener(MouseEvent.ROLL_OVER, rollOverHandler); s.addEventListener(MouseEvent.ROLL_OUT, rollOutHandler); s.addEventListener(MouseEvent.CLICK, clickHandler); addChild(s); } // mouse events private function rollOverHandler(event:MouseEvent):void { var glow:GlowFilter = event.currentTarget.filters[0]; Tweener.addTween(glow, { blurY:32, time:.25, onUpdate:tweenBlur, onUpdateParams:[glow, event.currentTarget] } ); } private function rollOutHandler(event:MouseEvent):void { var glow:GlowFilter = event.currentTarget.filters[0]; Tweener.addTween(glow, { blurY:0, time:.25, onUpdate:tweenBlur, onUpdateParams:[glow, event.currentTarget] } ); } private function clickHandler(event:MouseEvent):void { navigateToURL(new URLRequest(_link), "_blank"); } private function tweenBlur(glow:GlowFilter, s:Sprite):void { s.filters = [glow]; } } }

This is the main spinning wall of images here. Basically what this boils down to is a Sprite that adds instances of the above ThumbNail class to the display list and arranges them in a 3d circular pattern.

package { import flash.display.Loader; import flash.display.Sprite; import flash.events.Event; import flash.events.IOErrorEvent; import flash.net.URLRequest; public class CurvedImageWall extends Sprite { private var _thumbnailWidth:int; private var _thumbnailHeight:int; private var _radius:int; private var _currentLoadedImage:int; private var _numImages:int; private var _imageList:XMLList; private var _linkList:XMLList; public function CurvedImageWall(imgWidth:int, imgHeight:int, imgList:XMLList):void { _thumbnailWidth = imgWidth; _thumbnailHeight = imgHeight; _imageList = imgList.image; _linkList = imgList.link; _numImages = _imageList.length(); _currentLoadedImage = 0; // a little KLUDGE to play with _radius = ((_numImages * _thumbnailWidth) / 2 * Math.PI) / 9.5; loadImage(_imageList[_currentLoadedImage]); } private function loadImage(imageURL:String):void { var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageHandler); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, imageHandler); loader.load(new URLRequest(imageURL)); } private function imageHandler(event:*):void { event.currentTarget.removeEventListener(Event.COMPLETE, imageHandler); event.currentTarget.removeEventListener(IOErrorEvent.IO_ERROR, imageHandler); if (event is IOErrorEvent) { // trace ("couldn't load image #" + _currentLoadedImage); } else if (event is Event) { // image has loaded add it to 3d circle var angleIncrement:Number = 360 / _numImages; var tn:ThumbNail = new ThumbNail(_thumbnailWidth, _thumbnailHeight, event.currentTarget.content, _linkList[_currentLoadedImage]); var degrees:Number = (_currentLoadedImage * angleIncrement); var rads:Number = (degrees * Math.PI) / 180; var tx:Number = Math.cos(rads) * _radius; var tz:Number = Math.sin(rads) * _radius; tn.x = tx; tn.z = tz; tn.rotationY = Math.atan2(tx, tz) * 57.2957795; addChild(tn); } if (_currentLoadedImage++ < _numImages) loadImage(_imageList[_currentLoadedImage]); } } }

Finally, in the document class, main.as, we add an instance of the CurvedImageWall and set it spinning around in an ENTER_FRAME Event based on mouse position.

package { import flash.display.GradientType; import flash.display.Loader; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.events.Event; import flash.events.IOErrorEvent; import flash.filters.DropShadowFilter; import flash.geom.Matrix; import flash.net.URLLoader; import flash.net.URLRequest; /** * Example of circular curved 3d image wall in flash player 10 * @author Devon O. */ [SWF(width="500", height="400", backgroundColor="#000000", framerate="31")] public class main extends Sprite { private static const THUMB_WIDTH:int = 210; private static const THUMB_HEIGHT:int = 300; private var _wall:CurvedImageWall; private var _numImages:int; private var _centerX:Number; private var _centerY:Number; private var _rotationSpeed:Number; public function main():void { init(); } private function init():void { createBackground(); loadXML(); } private function createBackground():void { var filltype:String = GradientType.RADIAL; var colors:Array = [0x000000, 0x696969]; var alphas:Array = [1, 1]; var ratios:Array = [0x00, 0xFF]; var mat:Matrix = new Matrix(); mat.createGradientBox(stage.stageWidth, stage.stageHeight); var spread:String = SpreadMethod.REFLECT; graphics.beginGradientFill(filltype, colors, alphas, ratios, mat, spread); graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); graphics.endFill(); } private function loadXML():void { var ul:URLLoader = new URLLoader(new URLRequest("movies.xml")); ul.addEventListener(IOErrorEvent.IO_ERROR, xmlHandler); ul.addEventListener(Event.COMPLETE, xmlHandler); } private function xmlHandler(event:*):void { if (event is IOErrorEvent) { //trace ("couldn't load xml"); } else if (event is Event) { // xml has loaded - create image wall var movieXML:XML = new XML(event.currentTarget.data); var movieList:XMLList = movieXML.movie; _wall = new CurvedImageWall(THUMB_WIDTH, THUMB_HEIGHT, movieList); _centerX = stage.stageWidth * .5; _centerY = stage.stageHeight * .5; _wall.x = _centerX; _wall.y = _centerY; addChild(_wall); _wall.addEventListener(Event.ENTER_FRAME, frameHandler); } } private function frameHandler(event:Event):void { var wall:CurvedImageWall = event.currentTarget as CurvedImageWall; wall.z = (stage.mouseY - _centerY) - 200; _rotationSpeed = (_centerX - stage.mouseX) / 75; wall.rotationY += _rotationSpeed; wall.rotationX = (_centerX - stage.mouseX) / 100; wall.rotationZ = (_centerX - stage.mouseX) / 100; } } }

And all that yields (requires the Flash Player 10 plugin to view)…

[kml_flashembed movie=”http://blog.onebyonedesign.com/wp-content/uploads/2008/06/imagewall.swf” height=”400″ width=”500″ /]

Hope this might inspire something a little better. If so, share.

Date: