[AS3] XML Photo Gallery & Reflection Masking

Hello,

This post will be a bit long so please bear with me. I will organize my post to make it easier for you guys to read, and will, hopefully, cause less confusion.

[SIZE=“4”]**XML Photo Gallery **[/SIZE]

**Background **
Coming from a strong c++ programming background I find it a bit difficult to adjust to some of the quirks of Actionscript 3. Despite my initial fears I continued to push forward and learn more about the language and its capabilities.

I’m currently developing a portfolio site for a friend of mine who’s a wallpaper artist. One of the pages of this site will have a gallery which consists of the wallpapers she has made. With my lack of knowledge for Actionscript 3.0 I tackled the problem without a helping hand. Rather than following online tutorials I decided to figure things out for myself. I figured it would be
prudent to have the gallery powered by an XML file mainly because of the flexibility it presents, as in, being able to change the data in the XML file without the need to recreate go into Adobe Flash CS4 and recreate the SWF file.

**The Code **
WARNING: I have a bad habit of not commenting my code. The cause of this habit is simply the fact that I’m usually the only guy working on a project (even back in my c++ days).


package
{
	
    import flash.display.Bitmap ;
    import flash.display.Loader ;
	import flash.display.LoaderInfo ;
	import flash.display.MovieClip ;
	import flash.events.Event ;
	import flash.net.URLLoader ;
	import flash.net.URLLoaderDataFormat ;
	import flash.net.URLRequest ;
	
	public class WallpaperGallery extends MovieClip
	{
		
		private const WALLPAPERS_XML:String = "XML/Wallpapers.xml" ;
		private const WALLPAPER_WIDTH:uint  = 400 ;
		private const WALLPAPER_HEIGHT:uint = 300 ;

		private var _wallpapers:Array = new Array() ;		

		private var _data:XML = new XML() ;
		private var _imgCount:uint = 0 ;
		private var _loadCount:uint   = 0 ;
		
		private var _percentBatch:Number    = 0 ;
		private var _percentProgress:Number = 0 ;
		
		public function WallpaperGallery():void
		{
			
			this.addEventListener( Event.ADDED_TO_STAGE, addedToStage ) ;
		}
		
		private function addedToStage( e:Event ):void
		{
			
			var loader:URLLoader = new URLLoader() ;
			loader.dataFormat = URLLoaderDataFormat.TEXT ;
			loader.load( new URLRequest( WALLPAPERS_XML ) ) ;
			loader.addEventListener( Event.COMPLETE, xmlLoaded ) ;
		}

		private function xmlLoaded( e:Event ):void
		{

			try
			{

				_data  = XML( e.target.data ) ;
				_imgCount = _data.child( "*" ).length() ;
				
				_percentBatch = 100 / Number(_imgCount) ;				
				
				for( var n:uint = 0 ; n < _imgCount ; n++ )
				{
						
					var loader:Loader = new Loader() ;					
					loader.load( new URLRequest( _data.child( "node" )[n].url ) ) ;
				    loader.contentLoaderInfo.addEventListener( Event.COMPLETE, imgLoaded ) ;
				}
				
				addEventListener( Event.ENTER_FRAME, onEveryFrame ) ;				
			}
			
			catch( err:TypeError )
			{
				
				trace( "WallpaperGallery -- xmlLoaded" ) ;				
				trace( err.message ) ;
			}			
		}
		
		private function onEveryFrame( e:Event ):void
		{
			
			if( _loadCount == _imgCount )
			 cascadeImages() ;
		}
		
		private function imgLoaded( e:Event ):void
		{
			
			var m:uint = 0 ;
			
			_wallpapers.push( new Bitmap() ) ;
			m = _wallpapers.length ;
						
			_wallpapers[m-1] = Bitmap( LoaderInfo(e.target).loader.content ) ;
			
			_loadCount++ ;
			_percentProgress += _percentBatch ;
		}
		
		private function cascadeImages():void
		{
			
			for( var n:uint = 0 ; n < _imgCount ; n++ )
			{
				
				_wallpapers[n].x      = n * WALLPAPER_WIDTH ;
				_wallpapers[n].y      = 0 ;
				_wallpapers[n].width  = WALLPAPER_WIDTH ;
				_wallpapers[n].height = WALLPAPER_HEIGHT ;
				
				addChild( _wallpapers[n] ) ;				
			}
			
			removeEventListener( Event.ENTER_FRAME, onEveryFrame ) ;
		}
		
		public function get imageCount():uint{ return _imgCount ; }
	}	
	
}

  <?xml version="1.0" ?> 
  <wallpapers>
  <node>
  <id>0</id> 
  <title>This Beautiful Mess</title> 
  <url>Full/01.jpg</url> 
  </node>
  <node>
  <id>1</id> 
  <title>Carnelian</title> 
  <url>Full/02.jpg</url> 
  </node>
  <node>
  <id>2</id> 
  <title>Hiro Suzuhira</title> 
  <url>Full/03.jpg</url> 
  </node>
  </wallpapers>


**The Problem **
Without following any online tutorials I’ve managed to successful program an AS class that loads metadata from an XML file to render a cascading Wallpaper gallery. However, the main problem is that the images aren’t loaded in the exact order. Whichever image/wallpaper is done loading comes first in the list. Each image has a numeric name and I’d prefer to load each wallpaper in numeric order according to their names.

[COLOR=“red”]So how do I modify the code above to make it so that 01.jpg comes before 02.jpg? [/COLOR]

[SIZE=“4”]**Applying Matrix Transformation to a Reflection **[/SIZE]

**Background **
Oddly with this particular problem I wasn’t so bold in tackling it at all and decided to simply google. I managed to create a reflection class that takes the original MovieClip instance in its constructor and creates a reflection of that MovieClip instance. The online tutorial I followed to make such a class is located here:

http://www.everydayflash.com/blog/index.php/2008/04/13/reflection-effect-as3/

There are minor differences between the final product showcased in the above link and my code. But those differences are negligible. What I did in the code is add the original MovieClip and its reflection to a container MovieClip. From there I would attach an event listener to the container to listen for both MOUSE_OVER and MOUSE_OUT events. Ideally when the MOUSE_OVER event is triggered I would want both objects in the container to scale the same way. When MOUSE_OUT is triggered I would want them to be restored to their original size. Basically a zoom effect triggered by the mouse.

**Live Demo **
http://www.divinityzoo.org/help/HeartScaling.swf

**The Code **
**Reflection.as **


package
{
	
	import flash.display.Bitmap ;
	import flash.display.BitmapData ;	
	import flash.display.MovieClip ;
	import flash.geom.Point ;
	import flash.geom.Matrix ;
	import flash.filters.BlurFilter ;
	
	public class Reflection extends MovieClip
	{
		
		private var reflection:BitmapData ;
		private var reflectionHolder:Bitmap ;
		
		public function Reflection( orig:MovieClip )
		{
			
			var transform_mtrx:Matrix = new Matrix() ;
			transform_mtrx.scale( -1.0, 1.0 ) ;			
			transform_mtrx.rotate( Math.PI ) ;
			transform_mtrx.translate( orig.x, orig.y + (orig.height*2) ) ;
			
			reflection = new BitmapData( orig.width, orig.height, true, 0x00FFFFFF ) ;
			reflection.draw( orig ) ;
			
			reflection.lock() ;
			
			for( var n:uint = 0 ; n < reflection.height ; n++ )
			{
				
				var rowFactor:Number = 1 - ( n / reflection.height ) ;
				
				for( var m:uint = 0 ; m < reflection.width ; m++ )
				{
					
					var pixelColor:uint = reflection.getPixel32( m, n ) ;
					var pixelAlpha:uint = pixelColor >>> 24 ;
					var pixelRGB:uint = pixelColor & 0xFFFFFF ;
					var resultAlpha:uint = pixelAlpha * rowFactor ;
					
					reflection.setPixel32( m, n, resultAlpha << 24 | pixelRGB ) ;					
				}
			}
			
			reflection.unlock() ;
			
			reflectionHolder = new Bitmap( reflection ) ;			
			reflectionHolder.transform.matrix = transform_mtrx ;
			reflectionHolder.filters = [ new BlurFilter( 4, 4, 3 ) ] ;
			
			addChild( reflectionHolder ) ;
		}
	}	
	
}

How it’s used


var heartContainer:MovieClip = new MovieClip() ;
heartContainer.addChild( heart_mc ) ;

var heart_copy:Reflection = new Reflection( heart_mc ) ;
heartContainer.addChild( heart_copy ) ;

addChild( heartContainer ) ;

heartContainer.addEventListener( MouseEvent.MOUSE_OVER, onMouseOver_heart ) ;
heartContainer.addEventListener( MouseEvent.MOUSE_OUT, onMouseOut_heart ) ;

function onMouseOver_heart( e:MouseEvent ):void
{
	
	MovieClip(e.target).scaleX *= 1.4 ;
	MovieClip(e.target).scaleY *= 1.4 ;	
	
	removeEventListener( MouseEvent.MOUSE_OVER, onMouseOver_heart ) ;
}

function onMouseOut_heart( e:MouseEvent ):void
{
	
	MovieClip(e.target).scaleX *= 0.714285 ;
	MovieClip(e.target).scaleY *= 0.714285 ;
	
	removeEventListener( MouseEvent.MOUSE_OUT, onMouseOut_heart ) ;	
}


**The Problem **
I got everything working fine. However, whenever I apply a scaling transformation to the reflection I get unsual, and unexpected behavior. [COLOR=“red”]How do I remedy this?[/COLOR]

[SIZE=“4”]**Reflection Clipping **[/SIZE]

**Background **
The site I’m working on was initially designed in Photoshop. The idea was to first design the site in photoshop and code the functionality behind it in Flash. In Photoshop CS4 I’m able to successful create a clipping mask, as in, the reflections for all of the icons are rendered properly on the underlying background image.

**Live Demo **
http://www.divinityzoo.org/help/Clipping_failure.swf

**Screenshot **
Here is a screenshot taken from Photoshop CS4 on how the clipping is **supposed ** to look in the SWF file.
http://www.divinityzoo.org/help/Clipping_success.jpg

**The Code **
Not much to post really…


var heart_copy:Reflection = new Reflection( heart_mc ) ;
var ball_copy:Reflection = new Reflection( ball_mc ) ;
var umbrella_copy:Reflection = new Reflection( umbrella_mc ) ;

addChild( heart_copy ) ;
addChild( ball_copy ) ;
addChild( umbrella_copy ) ;

heart_copy.mask    = menu_mc ;
ball_copy.mask     = menu_mc ;
umbrella_copy.mask = menu_mc ;

**The Problem **
Basically I want the same clipping effect done in Photoshop to be replicated in Flash somehow. [COLOR=“red”]How do I accomplish this?[/COLOR]