coverflow

animated 3D interface to flip though images.

coverflow

src/nu/xero/gui/coverflow.as


package nu.xero.gui {
	import caurina.transitions.*;
	
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.media.*;
	import flash.net.*;
	
	import org.papervision3d.cameras.*;
	import org.papervision3d.core.render.data.*;
	import org.papervision3d.events.*;
	import org.papervision3d.materials.*;
	import org.papervision3d.objects.*;
	import org.papervision3d.objects.primitives.*;
	import org.papervision3d.view.*;
	
	public class coverflow extends BasicView {
		public  var stats			:RenderStatistics
		private var thisCover		:DisplayObject3D
		private var theCovers		:Array = new Array();
		private var coverCount		:Number;
		private var currentIndex	:Number = 0;
		private var planeAngle		:Number = 65;
		private var planeSeparation	:Number = 100;
		private var planeOffset		:Number = 200;
		private var needsRendered	:Boolean = false;
		private var audio			:Sound;
		private var isPlaying		:Boolean = false;
		
		public function coverflow(playlist:XMLList, art:Array, stageRef:Stage) {
			super(0,0, true, true, CameraType.TARGET);
			
			camera.zoom = 1;
			camera.focus = 500;
			camera.z = -800;
			
			stats = renderer.renderScene(scene, camera, viewport);
			
			coverCount = playlist.length();
			var i:Number = 0;
			while(i++ < coverCount) {
				theCovers[i] = new Object();
				theCovers[i].cover = new Plane(new BitmapMaterial(generateReflection(art[i-1].img)), 461, 492, 4, 4);
				theCovers[i].cover.name = playlist[i-1].title;
				theCovers[i].cover.material.interactive = true;
				theCovers[i].cover.material.doubleSided = false;
				theCovers[i].cover.material.smooth = false;
				theCovers[i].cover.extra = i;
				theCovers[i].cover.y -= 200; 
				theCovers[i].isZoomed = false;
				theCovers[i].mp3 = playlist[i-1].mp3;
				theCovers[i].cover.addEventListener(InteractiveScene3DEvent.OBJECT_MOVE, hover);
				theCovers[i].cover.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, hit);
				theCovers[i].cover.addEventListener(InteractiveScene3DEvent.OBJECT_OUT, out);
				stageRef.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheel);
				scene.addChild(theCovers[i].cover, "cover"+i);
			}
			flowCovers(int(coverCount*.5+1));
			startRendering();
		}
		override protected function onRenderTick(e:Event = null):void {
			if(needsRendered){
				super.onRenderTick(e);
				needsRendered=false;
			}
		}
		private function hit(e:InteractiveScene3DEvent):void {
			if(e.y <= 250){ //dont click the reflections
				thisCover = e.displayObject3D;
				flowCovers(thisCover.extra);
			}
		}
		private function hover(e:InteractiveScene3DEvent):void {	
			if(e.y <= 250){ //dont click the reflections			
				this.buttonMode = true;
			} else {
				this.buttonMode = false;
			}
		}
		
		private function out(e:InteractiveScene3DEvent):void {
			this.buttonMode = false;
		}
		private function mouseWheel(e:MouseEvent):void {
			if(e.delta < 0){
				if(currentIndex > 1){
					flowCovers(currentIndex-1);	
				}	
			} else {
				if (currentIndex < coverCount) {
					flowCovers(currentIndex+1);				
				}			
			}
		} 
		private function generateReflection(bmp:BitmapData):BitmapData{
			var reflectedBmp:BitmapData = new BitmapData(bmp.width, bmp.height*2, false, 0);
			reflectedBmp.draw(bmp);
			
			var alpha:Number = 0.3;
			var flipMatrix:Matrix = new Matrix(1, 0, 0, -1, 0, bmp.height*2 + 4);
			reflectedBmp.draw(bmp, flipMatrix, new ColorTransform(alpha, alpha, alpha, 1, 0, 0, 0, 0));
			
			var holder:Shape = new Shape();
			var gradientMatrix:Matrix = new Matrix();
			gradientMatrix.createGradientBox(bmp.width, bmp.height, Math.PI*.5);
			
			holder.graphics.beginGradientFill(GradientType.LINEAR, [ 0, 0 ], [ 0, 100 ], [ 0, 0xFF ], gradientMatrix);
			holder.graphics.drawRect(0, 0, bmp.width, bmp.height);
			holder.graphics.endFill();
			
			var m:Matrix  = new Matrix();
			m.translate(0, bmp.height);
			reflectedBmp.draw(holder, m);
			
			return reflectedBmp;	
		}		
		private function loadTrack(url:String):void {
			if(isPlaying) {
				SoundMixer.stopAll();
				audio = null;
			} else {
				isPlaying = true;
			}
			audio = new Sound(new URLRequest(url), new SoundLoaderContext(5000));
			audio.play();
		}		
		private function flowCovers(newIndex:*):void {
			if(currentIndex != newIndex) {
				needsRendered=true;
				var i:Number = 0;
				while(++i<=coverCount){
					thisCover = theCovers[i].cover;
					theCovers[i].isZoomed = false;
					//smoothing
					if(i>=newIndex-1 && i<=newIndex+1) {
						thisCover.material.smooth = true;
					} else {
						thisCover.material.smooth = false;
					}
					//tween covers
					if(i == newIndex) {
						Tweener.addTween(thisCover, {
							x: 0,
							z: -180,
							rotationY: 0,
							time: .8,
							transition: "easeOutExpo",
							onUpdate: function():void{  needsRendered = true; }
						});
					} else if(i < newIndex) {
						Tweener.addTween(thisCover, {
							x: (newIndex-i+1)*-planeSeparation-planeOffset,
							z: ((newIndex-i+1)*+planeSeparation)*.5,
							rotationY: -planeAngle,
							time: .8,
							transition: "easeOutExpo",
							onUpdate: function():void{  needsRendered = true; }
						});			
					} else {
						Tweener.addTween(thisCover, {
							x: ((i-newIndex+1)*planeSeparation)+planeOffset,
							z: ((newIndex-i+1)*-planeSeparation)*.5,
							rotationY: planeAngle,
							time: .8,
							transition: "easeOutExpo",
							onUpdate: function():void{  needsRendered = true; }
						});			
					}			
				}
				currentIndex = newIndex;
			} else {
				thisCover = theCovers[newIndex].cover;
				//zoom in on selected cover
				if(!theCovers[newIndex].isZoomed){
					Tweener.addTween(thisCover, {
						z: -390,
						time: 3,
						transition: "easeOutExpo",
						onUpdate: function():void{  needsRendered = true; }
					});
					theCovers[newIndex].isZoomed = true;
					loadTrack(theCovers[newIndex].mp3);
				} else {
					Tweener.addTween(thisCover, {
						z: -180,
						time: 3,
						transition: "easeOutExpo",
						onUpdate: function():void{  needsRendered = true; }
					});
					theCovers[newIndex].isZoomed = false;
				}
			}
		}		
	}
}

Download

raw zip tar