package { /******************************************************************************* * Copyright (c) 2009 Andreas Rozek * * * * Permission is hereby granted, free of charge, to any person obtaining a copy * * of this software and associated documentation files (the "Software"),to deal * * in the Software without restriction, including without limitation the rights * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * * copies of the Software, and to permit persons to whom the Software is fur- * * nished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included in * * all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIA- * * BILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * * THE SOFTWARE. * * * * Additionally, any modifications to the original Software must be clearly * * marked in a way, that the original author will never be considered as the * * author of these modifications! * *******************************************************************************/ import flash.display.Sprite; import flash.events.Event; import flash.events.ProgressEvent; import flash.events.TimerEvent; import flash.text.TextField; import flash.text.TextFormat; import flash.utils.Timer; import flash.utils.getTimer; import mx.events.FlexEvent; import mx.preloaders.IPreloaderDisplay; public class PreloaderDisplay extends Sprite implements IPreloaderDisplay { internal var minHideTime:int = 500; // min. time before display [msec] internal var maxHideTime:int = 1000; // max. time without display [msec] internal var minDisplayTime:int = 5000; // min. display time [msec] internal var BackgroundAlpha:Number; // ...as reported by preloader internal var BackgroundColor:uint; // dto. internal var BackgroundImage:Object; // dto. internal var BackgroundSize:String; // dto. internal var Display:Sprite;// reference to actual PreloaderDisplay instance internal var StageWidth:Number; // ...as reported by preloader internal var StageHeight:Number; // dto. internal var DisplayTimer:Timer; // sends update ticks every 100ms internal var MessageView:TextField; // displays a static message internal var CompletionView:TextField; // numeric progress display internal var ProjectionView:TextField; // displays remaining loading time internal var Progress:Number; // progress as reported by preloader internal var lastProgress:Number = 0; // progress at previous tick internal var lastTime:Number = 0; // (actual) time at previous tick internal var lastSpeed:Number = 0; // average loading speed at prev. tick internal var DisplayStartTime:int = 0; // when did the display come up? internal var ReadyToRun:Boolean = false; // is application ready to run? //------------------------------------------------------------------------------ // constructor //------------------------------------------------------------------------------ public function PreloaderDisplay() { super(); }; //------------------------------------------------------------------------------ // initialize performs the actual initialization //------------------------------------------------------------------------------ public function initialize ():void { //minDisplayTime = 5000; // uncomment for testing graphics.beginFill(BackgroundColor,BackgroundAlpha); graphics.drawRect(0,0,StageWidth,StageHeight); // might not be necessary graphics.endFill(); /**** create and place all required controls ****/ var LeftAligned:TextFormat = new TextFormat("_sans", 11, 0xFFFFFF, false,false,false, null,null, "left"); var RightAligned:TextFormat = new TextFormat("_sans", 11, 0xFFFFFF, false,false,false, null,null, "right"); MessageView = new TextField(); MessageView.text = ""; MessageView.defaultTextFormat = LeftAligned; MessageView.x = StageWidth/2-120; MessageView.width = 140; MessageView.y = StageHeight/2-24; MessageView.height = 20; addChild(MessageView); CompletionView = new TextField(); CompletionView.text = ""; CompletionView.defaultTextFormat = RightAligned; CompletionView.x = StageWidth/2+20; CompletionView.width = 100; CompletionView.y = StageHeight/2-24; CompletionView.height = 20; addChild(CompletionView); ProjectionView = new TextField(); ProjectionView.text = ""; ProjectionView.defaultTextFormat = LeftAligned; ProjectionView.x = StageWidth/2-120; ProjectionView.width = 240; ProjectionView.y = StageHeight/2+6; ProjectionView.height = 20; addChild(ProjectionView); /**** create and start a timer which periodically updates the display ****/ lastProgress = 0; lastTime = getTimer(); lastSpeed = 0; DisplayStartTime = 0; // not really necessary DisplayTimer = new Timer(100); DisplayTimer.addEventListener(TimerEvent.TIMER, onTimerTick); DisplayTimer.start(); }; //------------------------------------------------------------------------------ // get/set... property handlers //------------------------------------------------------------------------------ public function set backgroundAlpha (newAlpha:Number):void {BackgroundAlpha = newAlpha;}; public function get backgroundAlpha ():Number {return BackgroundAlpha;}; public function set backgroundColor (newColor:uint):void {BackgroundColor = newColor;}; public function get backgroundColor ():uint {return BackgroundColor;}; public function set backgroundImage (newImage:Object):void {BackgroundImage = newImage;}; public function get backgroundImage ():Object {return BackgroundImage;}; public function set backgroundSize (newSize:String):void {BackgroundSize = newSize;}; public function get backgroundSize ():String {return BackgroundSize;}; public function set preloader (actualDisplay:Sprite):void { Display = actualDisplay; Display.addEventListener(ProgressEvent.PROGRESS, onDownloadProgress); Display.addEventListener(Event.COMPLETE, onDownloadComplete); Display.addEventListener(FlexEvent.INIT_PROGRESS, onInitializationProgress); Display.addEventListener(FlexEvent.INIT_COMPLETE, onInitializationComplete); }; public function set stageHeight (newHeight:Number):void {StageHeight = newHeight;}; public function get stageHeight ():Number {return StageHeight;}; public function set stageWidth (newWidth:Number):void {StageWidth = newWidth;}; public function get stageWidth ():Number {return StageWidth;}; //------------------------------------------------------------------------------ // onDownload/Initialization/Progress/Complete actual progress event handlers //------------------------------------------------------------------------------ private function onDownloadProgress (theEvent:ProgressEvent):void { Progress = Math.round(theEvent.bytesLoaded/theEvent.bytesTotal*100); }; private function onDownloadComplete (theEvent:Event):void { Progress = 100; }; private function onInitializationProgress (theEvent:FlexEvent):void { /* nop */ }; private function onInitializationComplete (theEvent:FlexEvent):void { ReadyToRun = true; }; //------------------------------------------------------------------------------ // onTimerTick timer event handler //------------------------------------------------------------------------------ private function onTimerTick (theEvent:TimerEvent):void { //Progress = Math.round(getTimer()/minDisplayTime*100); // uncomment for testing if (Progress < 100) { // calculate current loading speed var ProgressDelta:Number = Progress-lastProgress; var TimeDelta:Number = getTimer()-lastTime; if (TimeDelta > 0) { // just to be on the safe side lastSpeed = lastSpeed*0.9 + ProgressDelta/TimeDelta*0.1; }; lastProgress = Progress; lastTime = lastTime+TimeDelta; }; /**** decide whether to hide or show the display ****/ if (DisplayStartTime == 0) { // indicates a hidden display if ( (lastTime < minHideTime) || // it's too early to show anything ((lastTime < maxHideTime) && ((100-Progress)/lastSpeed < 500)) ) {// it's not useful to show something (works even when lastSpeed == 0) //if (Progress < 100) return; // uncomment for testing if (ReadyToRun) { DisplayTimer.stop(); dispatchEvent(new Event(Event.COMPLETE)); // VERY important! }; return; // leave without displaying anything }; DisplayStartTime = lastTime; // we start showing something now }; /**** update progress display ****/ if (Progress < 100) { // continuous MessageView update is necessary! MessageView.text = "loading, please wait..."; } else { MessageView.text = "initializing, please wait..."; }; CompletionView.text = Progress + "%"; /**** update progress bar ****/ graphics.lineStyle(1, 0x808080); graphics.drawRect(StageWidth/2-120,StageHeight/2-4,240,8); graphics.lineStyle(0); graphics.beginFill(0xa0ff00); graphics.drawRect(StageWidth/2-119,StageHeight/2-3,Math.round(2.38*Progress),6); graphics.endFill(); /**** if usefull: display a projection of the loading process ****/ if ((Progress > 1) && (Progress < 100) && (lastSpeed > 0) && (lastTime > 1000)) { var Projection:Number = Math.round((100-Progress)/lastSpeed/1000); if (Projection < 1) { ProjectionView.text = "(less than a second left)"; } else { if (Projection < 2) { ProjectionView.text = "(approx. 1 second left)"; } else { ProjectionView.text = "(approx. " + Projection + " seconds left)"; }; }; } else { ProjectionView.text = ""; // effectively removes the projection display }; /**** check completion conditions ****/ if ((getTimer() >= DisplayStartTime+minDisplayTime) && ReadyToRun) { DisplayTimer.stop(); dispatchEvent(new Event(Event.COMPLETE)); // VERY important! }; }; }; }