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: