Skip to content


Time Machine Animation with Tweener

So I am trying to think of a way to create some type of “Time Machine” animation. Fortunately with the help of Tweener, this is what I came up with – Please note that this is a work in progress. You will need to click the movie first to activate the keyboard events.

So the number represents the key, and the fruit represents the images that will appear.

0 = blueberry
1 = pineapple orange banana
2 = coconut
3 = strawberry pomegranate
4 = blackraspberry
5 = lime lemon
6 = peach mango

Here are the classes, Document Class Main.as is first – please note that this is a rough draft of this animation, the preloading of images do not work that well and I will eventually need to add some setter and getter methods to the TimeMachine class – the final version to be posted some time in the future.. Also note that this demo actually loads the images in twice, my thought was that during the initial load it would essentially cache the images, but this is not working properly – if you refresh this page you will see what I mean:

package {
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.geom.Point;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.KeyboardEvent;
	import flash.text.TextFormat;
	import flash.text.TextField;
	import fl.controls.TextArea;
	import fl.controls.ScrollPolicy;
	import flash.display.DisplayObject;
    import flash.display.DisplayObjectContainer;
	import flash.events.TimerEvent;
    import flash.utils.setInterval;
    import flash.utils.clearInterval;

	import caurina.transitions.*;

	public class Main extends Sprite
	{
		// array of all images to be prelaoded before the movie begins:
		private var imgArray:Array;

		// the class to handle all the images
		private var imgPreload:MultipleImagePreload;

		//text area component
		private var output:TextArea;

		// Time Machine
		private var TimeMachine:TimeMachineAnimation;

		public function Main()
		{
			// set the stage
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;

			// for debugging
			showOutput();

			// preload all the images
			preloadImages();

		}

		/*
		PRELOAD ALL THE IMAGES

		*/
		private function preloadImages():void
		{

			imgArray = new Array();
			imgArray[0] = "http://manewc.com/projects/flash/i/infused-blueberry.jpg";
			imgArray[1] = "http://manewc.com/projects/flash/i/infused-pineappleorangebanana.jpg";
			imgArray[2] = "http://manewc.com/projects/flash/i/infused-coconut.jpg";
			imgArray[3] = "http://manewc.com/projects/flash/i/infused-strawberrypomegranate.jpg";
			imgArray[4] = "http://manewc.com/projects/flash/i/infused-blackraspberry.jpg";
			imgArray[5] = "http://manewc.com/projects/flash/i/infused-limelemon.jpg";
			imgArray[6] = "http://manewc.com/projects/flash/i/infused-peachmango.jpg";

			imgPreload = new MultipleImagePreload(imgArray);
			addChild ( imgPreload );

			// trace ( imgPreload.loadArray );

			stage.addEventListener ( Event.ENTER_FRAME, checkPreload );
		}

		private function checkPreload(e:Event):void
		{
			if ( imgPreload.chkLoaded )
			{
				Message ( "\n====================\nALL IMAGES LOADED\n====================\n" );

				stage.removeEventListener ( Event.ENTER_FRAME, checkPreload );

				Message ( "\n1. Start Animation\n" );
				BeginTimeMachine();
			}
		}

		/*
		BeginTimeMachine

		*/
		private function BeginTimeMachine():void
		{
			TimeMachine = new TimeMachineAnimation ( imgArray );
			addChild ( TimeMachine );

			TimeMachine.x = stage.stageWidth / 2;
			TimeMachine.y = stage.stageHeight / 2;

			// add the event
			stage.addEventListener (KeyboardEvent.KEY_DOWN, animTimeMachine);
		}

		private function animTimeMachine ( k:KeyboardEvent ):void
		{
			if ( TimeMachine.animationComplete )
			{
				switch ( k.keyCode )
				{
					case 48:
						TimeMachine.setIndex ( 0 );
						break;

					case 49:
						TimeMachine.setIndex ( 1 );
						break;

					case 50:
						TimeMachine.setIndex ( 2 );
						break;

					case 51:
						TimeMachine.setIndex ( 3 );
						break;

					case 52:
						TimeMachine.setIndex ( 4 );
						break;

					case 53:
						TimeMachine.setIndex ( 5 );
						break;

					case 54:
						TimeMachine.setIndex ( 6 );
						break;

					default:
						break;
				}
			}
		}

		/*
		FOR DEBUGGING

		*/
		private function showOutput():void
        {
			output = new TextArea();
			output.verticalScrollPolicy = ScrollPolicy.ON;
			output.condenseWhite = true;
			output.setSize(stage.stageWidth, 300);
			output.move(0, 680);
			addChild(output);
        }

		public function Message(msg:String):void
		{
			output.appendText ( msg );
		}

		private var childrenArr:Array;

		public function traceChildren(d:Sprite)
        {
			childrenArr = new Array();

            for ( var k:uint; k < d.numChildren; k++ )
            {
                var o:DisplayObject = d.getChildAt(k);  

                if ( o is DisplayObjectContainer )
                {
                    childrenArr.push ( o );
                }
                else
                {
					// childrenArr.push ( "" );
                    // trace ( o );
                }
            }

			return childrenArr;

        }  

	}
}
package
{
	import flash.display.Sprite;
	import flash.utils.Timer;
    import flash.events.TimerEvent;
	import flash.events.MouseEvent;
	import flash.events.KeyboardEvent;
	// tweener
	import caurina.transitions.*;

	public class TimeMachineAnimation extends Sprite
	{
		private var tArr:Array;
		private var imgArr:Array;
		private var ct:Number =			0;
		public var currentIndex = 		0;

		public function TimeMachineAnimation (a:Array)
		{
			// Re Assign
			tArr = a;

			// Create an Object for each item of the Array
			imgArr = new Array();
			tArr.forEach ( BuildObject );

			// Send off each item of the array to offscreen bottom
			imgArr.forEach( Position );  

			// begin the timer
			setTimer();
		}

		private function BuildObject(element:*, index:int, arr:Array):void
		{
			imgArr[index] = new LoadImage ( tArr[index] );
		}

		private function Position(element:*, index:int, arr:Array):void
		{
			imgArr[index].y = 1400;

			imgArr[index].scaleX = ( 1 / (imgArr.length-1) ) * index;
			imgArr[index].scaleY = ( 1 / (imgArr.length-1) ) * index;

			addChild ( imgArr[index] );
		}

		private function setTimer():void
		{
			ct = 0;

			var myTimer:Timer = new Timer(100, imgArr.length);
            myTimer.addEventListener("timer", timerHandler);
            myTimer.start();
		}

		private function timerHandler(e:TimerEvent):void
		{

			Tweener.addTween(imgArr[ct], {y: 0, time:.7, transition:"easeOutBack"});

			ct++;

			currentIndex = ct;

			if ( ct == imgArr.length )
			{
				setIndex ( imgArr.length );
			}
		}

		private var motionDirection:Number;
		private var motionCount:Number;
		private var mct:Number = 0;
		private var stopZ:Number;

		private var animCounter:Number = 0;
		public var animationComplete:Boolean;

		public function setIndex(i:Number):void
		{
			// update the animation counter
			animCounter = 0;

			// toggle for the animation status
			animationComplete = true;

			// need to know the current index
			motionDirection = currentIndex - i;

			stopZ = i;

			if ( motionDirection > 0 )
			{
				motionCount =  motionDirection;

				mct = imgArr.length;

				var closeTimer:Timer = new Timer(600, motionCount);
           		closeTimer.addEventListener("timer", moveTowards);
            	closeTimer.start();
			}
			else if ( motionDirection < 0 )
			{
				motionCount =  Math.abs(motionDirection);

				mct = imgArr.length;

				var awayTimer:Timer = new Timer(600, motionCount);
           		awayTimer.addEventListener("timer", moveAway);
            	awayTimer.start();
			}
		}

		private function moveTowards ( t:TimerEvent ):void
		{
			Tweener.addTween(imgArr[currentIndex], {scaleX: 1.5, time:.5, transition:"easeOutBack"});
			Tweener.addTween(imgArr[currentIndex], {scaleY: 1.5, time:.5, transition:"easeOutBack"});
			Tweener.addTween(imgArr[currentIndex], {alpha: 0, time:1.6, transition:"easeOutBack"});

			for ( var u:Number = currentIndex; u > 0; u-- )
			{
				Tweener.addTween(imgArr[currentIndex-u], {scaleX: 1, time:3, transition:"easeOut"});
				Tweener.addTween(imgArr[currentIndex-u], {scaleY: 1, time:3, transition:"easeOut"});
			}

			currentIndex--;

			animCounter++;

			if ( animCounter == motionCount ? animationComplete = true : animationComplete = false );

		}

		private function moveAway ( t:TimerEvent ):void
		{
			currentIndex++;

			imgArr[currentIndex].scaleX = 1.5;
			imgArr[currentIndex].scaleY = 1.5;
			imgArr[currentIndex].alpha = 0;

			Tweener.addTween(imgArr[currentIndex], {scaleX: 1, time:.8, transition:"easeOut"});
			Tweener.addTween(imgArr[currentIndex], {scaleY: 1, time:.8, transition:"easeOut"});
			Tweener.addTween(imgArr[currentIndex], {alpha: 1, time:1.5, transition:"easeOut"});

			animCounter++;

			if ( animCounter == motionCount ? animationComplete = true : animationComplete = false );

		}

	}

}
package
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.setInterval;
	import flash.utils.clearInterval;

	public class MultipleImagePreload extends Sprite
	{
		public var images:Array;
		public var chkLoaded:Boolean = false;

		private var imgArray:Array;

		// array of images.. onload event
		private var imgLoadArray:Array;

		// array of LoadImage class
		private var loadArray:Array;

		// variables for our timer events
		private var initLoadTimer:uint;
		private var preLoadTimer:uint;

		// variables for the preload process
		private var getTotalBits:Number; // the tally for the total bits
		private var imageTotalSize:Number;
		private var imagePreLoadSize:Number; // the tally for preloading

		private var preloader:Preloader;

		public function MultipleImagePreload(images:Array)
		{
			imgArray = images;

			// Load in the images.
			loadArray = new Array();

			for ( var i:uint; i < images.length; i++ )
			{
				// load the image
				loadArray[i] = new LoadImage( imgArray[i] );

				// display the image to stage
				addChild ( loadArray[i] );

				// send them off the stage
				loadArray[i].x = -9999;
			}

			// add the preloader
			preloader = new Preloader();
			addChild ( preloader );

			initLoadTimer = setInterval(onLoadImage, 500);
		}

		private function onLoadImage():void
		{
			var imgLoadArray = new Array();

			for ( var i:uint=0; i < imgArray.length; i++ )
			{
				getTotalBits = loadArray[i].getBits();

				if ( getTotalBits > 0 && loadArray[i].setComplete())
				{
					// check again.
					imgLoadArray[i] = loadArray[i];
				}
			}

			if ( imgLoadArray.length == imgArray.length )
			{
				clearInterval ( initLoadTimer );

				imageTotalSize = 0;

				// add up the size of all images
				for ( var u:uint=0; u < imgArray.length; u++ )
				{
					if ( loadArray[u].getBits() ) /* Safari is requiring this */
					{
						imageTotalSize += loadArray[u].getBits();

					}
				}

				Main(root).Message( "Total Size (bits): " + imageTotalSize + "\n" );

				// we now have recorded all file sizes.. now start the preloading
				preLoadTimer = setInterval(preLoadImage, 50);
			}
		}

		private var loadStat:Number;

		private function preLoadImage():void
		{
			imagePreLoadSize = 0;

			// add up the size of all images
			for ( var p:uint=0; p < imgArray.length; p++ )
			{
				imagePreLoadSize += loadArray[p].getBitsLoaded();
			}

			if ( ( imagePreLoadSize / imageTotalSize ) )
			{
				loadStat = Math.floor(100 * (imagePreLoadSize / imageTotalSize));

				if ( loadStat >= 100 )
				{
					// clear the timer
					clearInterval ( preLoadTimer );

					// set the boolean variable to true to notify all images are loaded
					chkLoaded = true;

					Main(root).Message ("100% Loaded\n");

					removeChild ( preloader );
					preloader = null;
				}
				else
				{
					Main(root).Message (Math.floor(100 * (imagePreLoadSize / imageTotalSize)) + "% Loaded\n");

					// update the preloader
					preloader.loadStatus ( Math.floor(100 * (imagePreLoadSize / imageTotalSize)) );
				}
			}
		}
	}
}
package {
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.events.IEventDispatcher;
    import flash.net.URLRequest;

    public class LoadImage extends Sprite {
        private var url:String;
		private var imageHolder:Sprite;
		private var imageHolderHolder:Sprite;
		public var LoadImageArray:Array = new Array();
		public var bits:Number;
		public var bitsLoaded:Number;
		public var totalBits:Number;
		private var loader:Loader;

		public var _myWidth:Number = 10;
		public var mywidth:Number;

		private var completeStatus:Boolean = false;

        public function LoadImage(url:String) {
			var loader:Loader = new Loader();
            configureListeners(loader.contentLoaderInfo);

            var request:URLRequest = new URLRequest( url );
            loader.load(request);

			imageHolder = new Sprite();
			addChild ( loader );

			loader.y = -222;
			loader.x = -350;

			mywidth = 1;
        }

        private function configureListeners(dispatcher:IEventDispatcher):void {
			dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
			addEventListener(Event.COMPLETE, completeHandler);

        }

        private function completeHandler(event:Event):void {
			removeEventListener(Event.COMPLETE, completeHandler);

			setComplete();
		}

		public function setComplete()
		{
			completeStatus = true;

			return ( completeStatus );
		}

		public function getBits()
		{
			return ( bits );
		}

		public function getBitsLoaded()
		{
			return ( bitsLoaded );
		}

		public function getWidth()
		{
			return ( imageHolder.width );
		}

		public function w()
		{
			return imageHolder.width;
		}

        private function progressHandler(event:ProgressEvent):void {
			bitsLoaded = event.bytesLoaded;
			bits = event.bytesTotal;
        }
    }
}
package
{
    import flash.display.Sprite;
    import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	import flash.events.Event;

    public class Preloader extends Sprite
    {
		public var tfloaded:TextField;

		public var loaded:Number;

        public function Preloader()
		{
			addEventListener ( Event.ADDED_TO_STAGE , init );
        }

		private function init(e:Event)
		{
			// Small Black Text Formatting
			var tf:TextFormat = new TextFormat();
			tf.font = "Paralucent-Light";
			tf.color = 0x4d86c6;
            tf.size = 30;
            tf.underline = false;
			tf.align = "center";

        	tfloaded = new TextField();
			tfloaded.defaultTextFormat = tf;
        	addChild(tfloaded);
        	tfloaded.width = 100;
            tfloaded.height = 100;
			tfloaded.x = 100;
        	tfloaded.y = 100;
            tfloaded.multiline = true;
            tfloaded.wordWrap = true;
			tfloaded.background = false;
			tfloaded.text = "";

			tfloaded.x = (stage.stageWidth / 2) - (tfloaded.width / 2);
		}

		public function loadStatus ( loaded:Number ):void
		{
			tfloaded.text = loaded + "%";
		}
    }
}

Posted in Actionscript 3, Preloading, Timer Methods, Tweener.


4 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Jonathan Dumaine says

    I managed to break it in about 10 seconds. But on the bright side I’ll also give you a simple, but crude, fix. If you push a lot of keyboard buttons really fast they all just disappear, then when it stops freaking out I kept getting this:

    TypeError: Error #1010: A term is undefined and has no properties.
    at TimeMachineAnimation/moveAway()
    at flash.utils::Timer/_timerDispatch()
    at flash.utils::Timer/tick()

    So when you add the event listener and set up your keyboard events here,

    # // add the event
    # stage.addEventListener (KeyboardEvent.KEY_DOWN, animTimeMachine);
    # }
    #
    # private function animTimeMachine ( k:KeyboardEvent ):void
    # {
    # if ( TimeMachine.animationComplete )
    # {
    # switch ( k.keyCode )
    # {
    # case 48:
    # // Remove the event listener here <– Edits
    # TimeMachine.setIndex ( 0 ); // do your stuff
    # // Add the event listener again
    # break;

    Again, it may not be fast, or elegant, but it fixed the problem I was having (plus doesn’t allow for abusive Tweener spam). By the way, nice use of Tweener! =)

  2. Jonathan Dumaine says

    Sorry. Wordpress doesn’t like spaces, the code looks jumbled!

  3. Dipu says

    Hi I am creating a cube according to the post on http://www.designscripting.com/2008/12/05/3d-cube-gallery-as3-papervision/

    But I cant control the cube by imposing a time interval.I removed the arrows and mouse events. Any help??

  4. Neal Fassnacht says

    Awesome. I was looking for something similar to this.



Some HTML is OK

or, reply to this post via trackback.