OBO_FlashBox – LightBox in Flash (with added 3d)

Just in for the holidays – a new LightBox JS implementation for Flash (with a Flash 10 3d Twist).

Here’s a quickie example made with Flex just to see what I’m on about. The main drawback seems to be the new Flash Player 10 3d features tend to blur the edges of the picture’s frame. Not entirely sure why that is, but If anyone can explain, please let me know and I’ll do what I can to fix it up.

If interested, the script is below. It requires TweenLite and the Tweener equations for the animation (I like to mix and match), though you’re welcome to rewrite it for another tweening engine.

The main FlashBox class:

package com.onebyonedesign.extras {
 
	import caurina.transitions.Equations;
	import com.onebyonedesign.extras.OBO_PreloadAni;
	import flash.display.DisplayObjectContainer;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.URLRequest;
	import flash.text.AntiAliasType;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import gs.OverwriteManager;
	import gs.TweenLite;
 
	/**
	 * LightBox for Flash flash. Requires TweenLite for animation.
	 * @author Devon O.
	 */
	public class OBO_FlashBox extends Sprite {
 
		private var _instructions:TextField;
		private static const INSTRUCTIONS_FORMAT:TextFormat = new TextFormat("_sans", 12, 0x000000);
 
		private var _cover:Sprite;
		private var _frame:Sprite;
		private var _frameHolder:Sprite;
 
		private var _parent:DisplayObjectContainer;
		private var _path:String;
 
		private var _preloader:OBO_PreloadAni;
 
		private var _loader:Loader;
 
		private var _imageWidth:int;
		private var _imageHeight:int;
 
		private static const COVER_COLOR:uint = 0x000000;
		private static const FRAME_COLOR:uint = 0xFFFFFF;
 
		/**
		 * 
		 * @param	attachTo		DisplayObjectContainer that will "hold" the flashbox. Must be in the display list.
		 * @param	pathToImage		String that describes the path to the image to load.
		 */
		public function OBO_FlashBox(attachTo:DisplayObjectContainer, pathToImage:String):void {
			trace (pathToImage);
			_parent = attachTo;
			_path = pathToImage;
			mouseChildren = false;
			buttonMode = true;
 
			init();
		}
 
		private function init():void {
			OverwriteManager.init();
			initTextfield();
			initDisplay();
		}
 
		private function initTextfield():void {
			_instructions = new TextField();
			_instructions.defaultTextFormat = INSTRUCTIONS_FORMAT;
			_instructions.mouseEnabled = false;
			_instructions.selectable = false;
			_instructions.autoSize = TextFieldAutoSize.LEFT;
			_instructions.antiAliasType = AntiAliasType.ADVANCED;
			_instructions.text = "Click to close.";
		}
 
		private function initDisplay():void {
			_cover = drawCover(COVER_COLOR, .75, _parent.stage.stageWidth, _parent.stage.stageHeight);
 
			addChild(_cover);
 
			_frame = drawFrame(FRAME_COLOR, 1, 100, 100);
 
			_frameHolder = new Sprite();
			_frameHolder.x = _cover.width * .5;
			_frameHolder.y = _cover.height * .5;
			_frameHolder.addChild(_frame);
			_frameHolder.buttonMode = true;
 
			_preloader = new OBO_PreloadAni(10, 10, "Loading image...");
			_frame.addChild(_preloader);
 
			addEventListener(MouseEvent.CLICK, remove);
			_frameHolder.addEventListener(MouseEvent.CLICK, remove);
 
			_parent.addChild(this);
			_parent.addChild(_frameHolder);
			loadImage();
		}
 
		private function loadImage():void {
			_loader = new Loader();
			_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoad);
			_loader.load(new URLRequest(_path));
		}
 
		private function onImageLoad(event:Event):void {
			_frame.removeChild(_preloader);
			_imageHeight = Math.round(_loader.height + 40);
			_imageWidth = Math.round(_loader.width + 20);
			TweenLite.to(_frame, .3, { height:_imageHeight, ease:Equations.easeOutQuad } );
			TweenLite.to(_frame, .3, { width:_imageWidth, ease:Equations.easeOutQuad, delay:.3, onComplete:displayImage } );
		}
 
		private function displayImage():void {
			_loader.x = _loader.width * .5;
			_loader.y = -(_loader.height * .5 + 10);
			_loader.scaleX = -1;
			_loader.visible = false;
			_frameHolder.addChild(_loader);
			_instructions.x = 10 - _imageWidth * .5 + _instructions.textWidth;
			_instructions.y = _imageHeight * .5 - 20;
			_instructions.rotationY = 180;
			_instructions.visible = false;
			_frameHolder.addChild(_instructions);
			TweenLite.to(_frameHolder, .5, { rotationY:-180, onUpdate:checkRotation } );
		}
 
		private function checkRotation():void {
			if (_frameHolder.rotationY <= -90) {
				_loader.visible = true;
				_instructions.visible = true;
			}
		}
 
		private function drawCover(colour:uint, alpha:Number, w:Number, h:Number):Sprite {
			var s:Sprite = new Sprite();
			s.graphics.beginFill(colour, alpha);
			s.graphics.drawRect(0, 0, w, h);
			s.graphics.endFill();
			return s;
		}
 
		private function drawFrame(colour:uint, alpha:Number, w:Number, h:Number):Sprite {
			var s:Sprite = new Sprite();
			s.graphics.beginFill(colour, alpha);
			s.graphics.drawRect(-(w * .5), -(h * .5), w, h);
			s.graphics.endFill();
			return s;
		}
 
		private function remove(event:MouseEvent):void {
			_parent.removeChild(this);
			_parent.removeChild(_frameHolder);
		}
	}
}

And, while you can use any little graphic preloader you’d like, I used the one below:

package com.onebyonedesign.extras {
 
	import flash.display.Sprite;
	import flash.geom.ColorTransform;
	import flash.geom.Transform;
	import flash.text.AntiAliasType;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import flash.events.Event;
 
	public class OBO_PreloadAni extends Sprite {
 
		private var _r:Number;
		private var _numItems:uint;
 
		private var _p:Sprite;
		private var _items:Array;
 
		private var _timer:Timer;
		private var _ct:ColorTransform;
		private var _currItem:int;
 
		private var _words:String;
 
		public function OBO_PreloadAni(radius:Number, numItems:uint, message:String = "") {
 
			_r = radius;
			_numItems = numItems;
			_p = new Sprite();
			_words = message;
 
			_items = new Array();
			_currItem = 0;
 
			init();
		}
 
		private function init():void {
			addChild(_p);
			createItems();
			addText();
			startAnimation();
			addEventListener(Event.REMOVED_FROM_STAGE, kill);
		}
 
		private function createItems():void {
			var angleIncrement:Number = 360 / _numItems;
			for (var i:int=0; i <= _numItems; i++) {
				var item:Sprite = createItem(i);
				var degrees:Number  = (i * angleIncrement);
				var rads:Number = (degrees * Math.PI) / 180;	
 
				//end X and Y values for the card
				var tx:Number = Math.cos(rads) * _r;
				var ty:Number = Math.sin(rads) * _r;
 
				// get distance clip is from center
				var dx:Number = tx;
				var dy:Number = ty;
				// Computing the arctangent to find angle
				// atan2 is used to find the direction from one point to another in 2-dimension
				// Steradians (square radians) can be converted to square degrees by multiplying 
				// by the square of the number of degrees in a radian = 57.2957795... degrees. 
				// It is used to describe two-dimensional angular spans in three-dimensional space,
				item.rotation = Math.atan2(dy, dx) * 57.2957795;
				item.x = tx;
				item.y = ty;
				_items.push(item);
 
				_p.addChild(item);
			}
		}
 
		private function addText():void {
			if (_words) {
				var tf:TextField = new TextField();
				tf.selectable = false;
				tf.autoSize = TextFieldAutoSize.LEFT;
				tf.mouseEnabled = false;
				tf.defaultTextFormat = new TextFormat("_sans", 11);
				tf.antiAliasType = AntiAliasType.ADVANCED;
				tf.text = _words;
				tf.y = Math.round(_p.height / 2 + 5);
				tf.x = Math.round(- tf.textWidth / 2);
				addChild(tf);
			}
		}
 
		private function startAnimation():void {
			_timer = new Timer(40);
			_timer.addEventListener(TimerEvent.TIMER, updateColor);
			_timer.start();
		}
 
		private function updateColor(te:TimerEvent):void {
			// change old to black
			var t:Sprite = _items[_currItem];
			_ct = new ColorTransform();
			t.transform.colorTransform = _ct;
 
			if (++_currItem > _items.length - 1) _currItem = 1;
 
			// change new to reddish
			t = _items[_currItem];
			_ct = new ColorTransform();
			_ct.redMultiplier = 1;
			_ct.redOffset = 0x88;
			_ct.greenMultiplier = 0;
			_ct.blueMultiplier = 0;
			t.transform.colorTransform = _ct;
 
		}
 
		private function createItem(id:Number):Sprite {
			var r:Sprite = new Sprite();
			r.graphics.beginFill(0x000000);
			r.graphics.drawRect(0, 0, 8, 3);
			r.graphics.endFill();
			return r;
		}
 
 
		public function destroy():void {
			_timer.stop();
			_timer.removeEventListener(TimerEvent.TIMER, updateColor);
			_ct = new ColorTransform();
			_items.forEach(setBlack);
			_currItem = _items.length - 1;
			_timer = new Timer (20, _items.length);
			_timer.addEventListener(TimerEvent.TIMER, removeItem);
			_timer.addEventListener(TimerEvent.TIMER_COMPLETE, onDone);
			_timer.start();
		}
 
		private function setBlack(e:Sprite, i:uint, a:Array):void {
			e.transform.colorTransform = _ct;
		}
 
 
		private function removeItem(te:TimerEvent):void {
			_p.removeChild(_items[_currItem--]);
		}
 
		private function onDone(te:TimerEvent):void {
			_timer.removeEventListener(TimerEvent.TIMER, removeItem);
			_timer.removeEventListener(TimerEvent.TIMER_COMPLETE, onDone) 
			dispatchEvent(new Event("onPlaDone"));
		}
 
		private function kill(event:Event):void {
			removeEventListener(Event.REMOVED_FROM_STAGE, kill);
			_timer.stop();
			_timer.removeEventListener(TimerEvent.TIMER, updateColor);
 
			_p = null;
			_items = null;
			_timer = null;
			_ct = null;
		}
	}
}

Unfortunately, to get the OBO_FlashBox class to play nicely with Flex, you need to create a little Flex “bridge” that extends UIComponent like so:

package  {
 
	import flash.events.Event;
	import mx.core.UIComponent;
	import com.onebyonedesign.extras.OBO_FlashBox;
 
	/**
	 * little class to use OBO_FlashBox in Flex
	 * @author Devon O.
	 */
	public class FlexBox extends UIComponent {
 
		private var _pathToImage:String;
 
		public function FlexBox(pathToImage:String) {
			_pathToImage = pathToImage;
			if (stage) {
				init(null);
			} else {
				addEventListener(Event.ADDED_TO_STAGE, init)
			}
		}
 
		private function init(event:Event):void {
			removeEventListener(Event.ADDED_TO_STAGE, init)
			var _fb:OBO_FlashBox = new OBO_FlashBox(this, _pathToImage);
		}
	}
}

And finally, the mxml for the example shown:

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
	<mx:Script>
		<![CDATA[
 
		import mx.events.ListEvent;
 
		[Bindable]
		[Embed(source="assets/thumbs/1.jpg")]
		public var thumb1:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/2.jpg")]
		public var thumb2:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/3.jpg")]
		public var thumb3:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/4.jpg")]
		public var thumb4:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/5.jpg")]
		public var thumb5:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/6.jpg")]
		public var thumb6:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/7.jpg")]
		public var thumb7:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/8.jpg")]
		public var thumb8:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/9.jpg")]
		public var thumb9:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/10.jpg")]
		public var thumb10:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/11.jpg")]
		public var thumb11:Class;
 
		[Bindable]
		[Embed(source="assets/thumbs/12.jpg")]
		public var thumb12:Class;
 
		private var _flexBox:FlexBox;
 
		private function displayImage(event:ListEvent):void {
			if (_flexBox && parent.contains(_flexBox)) {
				parent.removeChild(_flexBox);
				_flexBox = null;
			}
			_flexBox = new FlexBox(event.currentTarget.selectedItem.path);
			parent.addChild(_flexBox);
		}
		]]>
	</mx:Script>
	<mx:Panel title="OBO_FlashBox Example" width="525" height="430">
		<mx:TileList id="CameraSelection" itemClick="displayImage(event)" height="100%" width="100%" maxColumns="4" rowHeight="125" columnWidth="125">
			<mx:dataProvider>
				<mx:Array>
					<mx:Object label="Monkeys" icon="{thumb1}" path="images/large/1.jpg" />
					<mx:Object label="Maniacs" icon="{thumb2}" path="images/large/2.jpg"/>
					<mx:Object label="Dead" icon="{thumb3}" path="images/large/3.jpg"/>
					<mx:Object label="Feast" icon="{thumb4}" path="images/large/4.jpg"/>
					<mx:Object label="Cyclops" icon="{thumb5}" path="images/large/5.jpg"/>
					<mx:Object label="House" icon="{thumb6}" path="images/large/6.jpg"/>
					<mx:Object label="Beyond" icon="{thumb7}" path="images/large/7.jpg"/>
					<mx:Object label="Bug" icon="{thumb8}" path="images/large/8.jpg"/>
					<mx:Object label="Dead" icon="{thumb9}" path="images/large/9.jpg"/>
					<mx:Object label="Zombies" icon="{thumb10}" path="images/large/10.jpg"/>
					<mx:Object label="Hell" icon="{thumb11}" path="images/large/11.jpg"/>
					<mx:Object label="Christmas" icon="{thumb12}" path="images/large/12.jpg"/>
				</mx:Array>
			</mx:dataProvider>
		</mx:TileList>
	</mx:Panel>
</mx:Application>

Hope it may be useful. Enjoy…

13 Comments »

  1. noponies says:

    Nice example, as always!

  2. john says:

    error at _flexBox = new FlexBox(event.currentTarget.selectedItem.path);

  3. Devon O. says:

    Thank you, noponies..

    And what’s the error, john? I’m not seeing it thrown.

  4. James says:

    Very Nice, I have been searching all over for this, but all I’m getting is empty white boxes, I can’t seem to get the image to load in the flash box… same goes for your linked example?

  5. Devon O. says:

    Hey James,

    I apologize if this is a stupid question but are you using Flash CS4/Flash Player 10?

  6. James says:

    Apparently I’m the stupid one, I just upgraded to Flash 10 for Safari and now your demo appears to be working… although Flex Builder is now throwing an error in OB_FashBox.as:

    1119: Access of possibly undefined property rotationY through a reference with static type flash.display:Sprite.
    OBO_FlashBox.as line 128

    and

    1119: Access of possibly undefined property rotationY through a reference with static type flash.text:TextField.
    OBO_FlashBox.as line 121

    both seem to have issue with the rotationY function…

  7. Devon O. says:

    Aha. Now you’ll have to make sure you’re using the most recent Flex SDK. I don’t use Flex Builder (just FlashDevelop pointed towards the free SDK), so I couldn’t help you there, but it shouldn’t be too difficult to figure out.

  8. James says:

    what would I have to do to eliminate the flip effect?

  9. Devon O. says:

    Hey James, Just go through the displayImage() method of OBO_FlashBox.as and play with the settings. In that method, _loader is the picture and _instructions is the textfield that just says “click to close”. Change their x and y positions and get rid of the scaleX setting on the _loader. Don’t set their visible properties to false and get rid of the TweenLite call (that does the flip).

  10. James says:

    Thanks, I got it working, I didn’t realize that scaleX was actually flipping the image, it was driving me nuts..
    … you my friend, have officially earned Coffee on me :)

  11. James says:

    also for anyone else that stops by: To run these scripts in Flex Builder you will need to import fl.motion.Motion for the TweenLite calls (although I never got rotateY to work) – the file is available in your Flash install directory

  12. Devon O. says:

    Thank you for the info, James – and, moreover, thanks for the coffee!

  13. pradeep says:

    hi that flash light box is seem good i want this

RSS feed for comments on this post. / TrackBack URI

Leave a Reply

Devon O. Wolfgang

Technical Reviewer of “The Essential Guide to Flash CS4 AIR Development”

Contributing Author of “Flash AS3 for Interactive Agencies”

Senior Software Developer in Dublin, Ireland

Portfolio

Bayer Pixel Bender Filter

Bayer Mosaic Filter in Pixel Bender


Now, seeing as how I’ve been trying to learn some Pixel Bender coding lately[...]

Liquitext

Liquid Text Effect


Haven’t been too active around here lately due to a major on going project at work lately as well as the fact that my wife and I are in the slow process of moving homes. Really, the only free time it seems I have these days is during the lunch hour. So, over the past two days, I came up with this fun little toy during lunches[...]

Google’s Text To Speech Engine in Flash

I’m not sure how I managed to miss this, but I just happened to run across a ‘new’ (well, newish), albeit still unofficial, offering of Google today: text-to-speech. You can see what few details there are on this Techcrunch post. Basically, it just boils down to this though – you send your phrase to be [...]

Quick Sound Effects Generator


Need some beeps, boops, or bops, to go with your UI or games [...]

Quick QuickBox2D Tip II – Collision Detection

Custom collision detection/handling in QuickBox2D [...]

None of This Runs Eternal


No Flash/Actionscript stuff here. Just me rambling about the upcoming Current 93 concert [...]

Playing Around with the New UndoManager

Included in the Flex 4.0 SDK and the, just released, Flash Professional CS5 lies a new hidden little gem of a class: flashx.undo.UndoManager (although the Flex 4.0 SDK’s been out for awhile, I have to admit I didn’t even notice this until I installed Flash CS5 and started poking around the documentation looking for new [...]

Making Waves

In a previous post, “Digging into the Microphone in Flash Player 10.1″, reader David Law asked in the comments how it would be possible to save .wav files to the server. I wasn’t sure right offhand, but thought I’d spend my lunch hour yesterday looking into it. Well, after reading this quick tutorial on Adobe [...]

Some Drawing Fun and a Bad Movie You’ll Never See

Earlier today, Dave Gillem posted a link on Facebook to an incredible Processing based drawing application. Thought I’d have a go at reproducing something similar in Flash. Well, I failed miserably, but the results were still interesting enough to check out. You can play around with it below. Just mouse down to draw in the [...]

Animating Bezier Curves


The other day I got the notion in my head that I wanted to draw some bezier curves in an animated fashion. I’m sure this has been done a million times before, but sometimes reinventing the wheel can be a good learning experience [...]

My God, It’s Full of Stars…


Just a quick little thing I got an idea for (i.e. pretty much ripped off) while browsing Processing examples.

Another Year, Another Look

As a few folks may have noticed, the blog is looking a little bit different as of today [...]

The Webcam Warholizer


Just a little bit of quick Friday fun with webcam and bitmapata…

More With the JiglibFlash Terrain

Playing around a bit more with the animated JiglibFlash terrain idea from my last post, I wanted to see how it would work in conjunction with a webcam. So, needless to say, you’ll need a webcam to check out the results below.

Rockin and Rollin with the JiglibFlash Terrain


Finally got the chance to play around with the new JiglibFlash height map terrain, today, and have to say I am very impressed [...]