During this tutorial I will demonstrate how to make a custom timer which supports counting up or down to a specified amount of time.
Final Result Preview
In the demo below you will see the time counting down from 1:00, a progress bar that tracks the progress of the timer and a cancel button.
Once the countdown from the 1:00 is finished, or you press the cancel button, a Movie Clip slides in from the right. This Movie Clip lets you enter a desired time, and determines whether you have the time counting up or down. A progress bar will then be attached and will track the progress of the time. Once the time completes, or you press the cancel button, you will be able to enter a new time.
Step 1: Coding the Custom Timer Class
Here we will write the custom timer class that will drive our main movie.
Go to File > New and Choose Actionscript 3.0 Class. Save this file as "CountTimer.as".
Step 2: Import Classes
Here we open the package and import the classes that we will need to write this class.
Add the following to the "CountTimer.as"
package { //Needed for the Timer import flash.utils.Timer; import flash.events.TimerEvent; //We will update this TextField with the time import flash.text.TextField;
Step 3: Variables and Constructor Function
Here we will declare the variables the class will need, and code the constructor function.
Add the following to the "CountTimer.as".
public class CountTimer extends Timer { //The textfield where the updated time goes private var targetText:TextField; //The direction of the timer i.e "up" or "down" private var direction:String; //How many minutes the timer has private var minutes:int; //How many seconds the timer has private var seconds:int; //Used in our calculation to update the timer private var totalSeconds:int; //The total amount of time = (Minutes+Seconds) private var timeTotal; //How much of the time has Passed private var timeLoaded = 0; //Set to true to test the timer private var test:Boolean = false; public function CountTimer(min:int,sec:int,dir:String,targetTextField:TextField=null) { minutes = int(min * 60); seconds = int(sec); timeTotal = minutes + seconds; super(1000,timeTotal); if (dir == "down") { totalSeconds = minutes + seconds; } else { totalSeconds = 0; } if (targetTextField != null) { targetText = targetTextField; } direction = dir; }
Here we declared some variables we need for our class.
The Constructor Function takes four parameters; they are as follows:
-
min
: An integer value that represents how many minutes you wish the timer to have. -
sec
: An integer value that represents how many seconds you wish the timer to have. -
dir
: A string representing the direction of the counter can be one of the following: "up" or "down". -
targetTextField
: A TextField that will be used to show the updated time (This is optional).
Inside the constructor function we set the minutes
variable equal to the specified value of min
* 60 (since 1 minute is equal to 60 seconds).
We then set the seconds
variable equal to the sec
.
Next we set the timeTotal
variable equal to minutes
+ seconds
.
Then we call super()
-- which run the code from the constructor function of the Timer
class -- pass in 1000 milliseconds, and the timeTotal
variable. By using 1000 milliseconds we tell the timer to tick one time each second, and by passing in timeTotal
we tell the timer how many times to fire.
We next check whether the dir
parameter is equal to "up" or "down" and set the totalSeconds
variable accordingly.The total seconds will be used in our timeHandler()
function. We will increment or decrement this by 1 each timer the timer fires off.
Then we check whether or not the user has passed in a TextField to the constructor and set the targetText
variable if they did; this will be used to display the time.
Finally we set the direction
variable to the direction the user passed in.
Step 4: Coding the start()
Function
Here we code the start()
function for our timer.
Add the following the the to the "CountTimer.as".
override public function start():void { super.start(); addEventListener(TimerEvent.TIMER, timerHandler); }
Because we extended Timer we are overriding the Timer's existing start()
method. Here we simply call super.start()
(which runs the code from the Timer class's start()
method) and add an EventListener to detect when the timer ticks.
Step 5: Coding the timerHandler
Function
Here we code our timerHandler()
function. This is where we update our time and display it. This function is called once every second.
Add the following to the CountTimer class.
private function timerHandler(e:TimerEvent):void { //Update our time Loaded variable timeLoaded += 1; if (direction == "up") { //totalSeconds is = 0, to start with. We add 1 to it. totalSeconds++; } else { //totalSeconds = theNumber of seconds by adding min and sec; we subtract 1 from it totalSeconds--; } //How may seconds there are left. seconds = totalSeconds % 60; //How many minutes are left minutes = Math.floor(totalSeconds / 60); //The minutes and seconds to display in the TextField. var minutesDisplay:String = (minutes < 10) ? "0" + minutes.toString() : minutes.toString(); var secondsDisplay:String = (seconds < 10) ? "0" + seconds.toString(): seconds.toString(); if (targetText != null) { targetText.text = minutesDisplay + ":" + secondsDisplay; } if (test=true) { trace(minutesDisplay + ":" + secondsDisplay); } }
Inside this function we update our timeLoaded
variable, which is used to track the progress of how much time has passed.
Next we check whether direction
is equal to "up" or "down" and adjust our totalSeconds
variable accordingly.
We next determine how many seconds and minutes are left; pad the minutesDisplay
and secondsDisplay
with an extra zero if necessary; update the TextField (if one was passed into the constructor); and optionally trace out the time if we've set our test
variable to true (handy for testing if you don't set a TextField).
The calculation used to determine the seconds
variable takes the remainder of totalSeconds/60
.The % (modulo) operator gives us the remainder.
The calculation used to determine the minutes
variable simply takes the totalSeconds
/60, rounded down using Math.floor()
.
Step 6: Coding the getTimeTotal()
Function
Here we code a function that simply returns our timeTotal
variable.Because we made this variable private we need a way to access it.
public function getTimeTotal():int { return timeTotal; }
Step 7: Coding the getTimeLoaded()
Function
Here we code a function that returns our timeLoaded
variable. Once again because it is private we need a way to access it.
public function getTimeLoaded():int { return timeLoaded; }
Step 8: Coding the getProg()
Function
This function gives us the progress of our time.It is how much of the total time (that we set in the constructor) has passed. We multiply by 100 so we get a percentage between 1 and 100.
public function getProg():int { return Math.floor(timeLoaded/timeTotal*100); }
Step 9: Testing the CountTimer Class
Here we begin to design the main movie that uses our "CountTimer" class.
Go to File > New and create a new ActionScript 3.0 document.
Set the size to 320x220px.
Save this document as "Timer.fla". Go to File > New and choose Actionscript 3.0 Class.
Save this file as "Main.as". We are going to test our CountTimer class,so add the following to the "Main.as".
package { import flash.display.MovieClip; import CountTimer; public class Main extends MovieClip{ var countTimer:CountTimer; public function Main() { countTimer = new CountTimer(1,30,"up"); countTimer.start(); } } }
Make sure you set the test
variable in "CountTimer.as" to true
, and set the movie's Document Class to "Main".
Press ctrl-enter and test the movie; you should see the time traced in the Output window.
Step 10: Designing the User Interface
Here we begin to design the main movie that uses our CountTimer class.
We will be using TweenLite in our movie so if you don't have a copy get it from greensock.com.
Countdown/Countup Text
Select the Text tool and make sure the following properties are set in the "CHARACTER" Panel.
- Size: 50.0 pt
- Color: Black
Still in the "Character" panel, click on "Embed" and make sure "UpperCase","LowerCase","Numerals" are selected, and under "Also include these characters"
that you have added a ":" and a "%".
Draw a textfield out on the stage and set the following properties under the "POSITION and SIZE" Panel.
- X: 0
- Y: 0
- W: 320
- H: 60
Give this TextField the instance name "timer_txt"; make sure the type is set to "Classic Text" and "Dynamic Text" respectively.
This TextField will be used to show the time when the movie first Starts.
Progress Bar
Now we need to design our progress bar.
Go to Insert > New Symbol. Give it the name "ProgressContainer" and make sure "Export for Actionscript" is checked and that the Class is set to "ProgressContainer".
Go to Window > Components and drag a ProgressBar component into the movie clip.
Set the ProgressBar's properties to the following.
- X: 0
- Y: 0
- W: 320
- H: 42.0
Under "Component Parameters" set mode to "manual".
Give the ProgressBar the instance name "pb".
Select the Text tool and make sure the following properties under the "CHARACTER" Panel are set.
- Size: 30pt
- Color: black
Draw a TextField into the movie clip.
Set the following properties on the TextField.
- X: 80.0
- Y: 1.0
- W: 159.0
- H: 38:0
Give this TextField the instance name "progress_txt".
You can now close this MovieClip.
Growing and Fading Text
Now we will design the growing and fading text, to be displayed when all the time has elapsed.
Go to Insert > New Symbol.Give it the name "GrowFadeText" and make sure "Export for Actionscript" is checked and the Class is set to "GrowFadeText".
Select the Text tool and make sure following properties are set under the "Character" Panel.
- Size: 15pt
- Color: [I set it to a blue, you can do the same if you wish]
Drag out a TextField into the MovieClip and set the following properties.
- X: -69.0
- Y: -6.35
- W: 135.0
- H: 21.0
Enter the text "TIMER COMPLETE" into the textField. You can now close this MovieClip.
Entry Fields
Now we will design the MovieClip that slides in from the left. Select the rectangle tool and set the color to white. Drag out a rectangle from the top left of the stage to the bottom right.
Select the rectangle you just dragged out and press F8.Give it the name "countContainer".Make sure "Export for Actionscript is checked and that Class is set to "countContainer".
Set the following properties on the MovieClip.
- X: 322
- Y: 0
- W: 320
- H: 220
Now double click to go inside the MovieClip.
Select the Text tool and make sure the following properties are set in the "CHARACTER" Panel.
- Size: 50pt
- Color: Black
Drag out a textfield onto the stage and set the following properties on it.
- X: 0
- Y: 0
- W: 320
- H: 60
Give this TextField the instance name "timer_txt" and make sure the type is set to "Classic Text" and "Input Text" respectively.
Once again select the Text tool and drag a TextField onto the stage.Then set the following properties on it.
- X: 0
- Y: 59.0
- W: 320
- H: 60
Give this TextField the instance name "instructions_txt" and make sure the type is set to "Classic Text" and "Input Text" respectively.
Go to Window > Components and drag three buttons inside this MovieClip.
Give the first button the instance name "countDown_btn" and set the following properties on it.
- X: 14.00
- Y: 160
Give the second button the instance name "cancel_btn" and set the following properties on it.
- X: 103.00
- Y: 160.00
Give the third button the instance name "countUp_btn" and set the following properties on it.
- X: 182.00
- Y: 160.00
You can now close the MovieClip.
Now that we have our UI Designed we can write the code for the main movie.
Step 11: Coding the Main Movie
If you are following along delete all the ActionScript in "Main.as" that you created in Step 9. That was for testing; now we need code that will work with our new interface.
Here we open the package declaration and import the classes we will be using.
Add the following to "Main.as".
package { import flash.display.MovieClip; import fl.controls.Button; import flash.text.TextField; import flash.events.TimerEvent; import flash.events.MouseEvent; import flash.text.TextFieldType; import flash.text.TextFieldAutoSize; import CountTimer; import com.greensock.TweenLite; import flash.events.Event; import fl.controls.ProgressBar;
Step 12: Variables and Constructor Function
Here we declare our variables and code the Constructor.
Add the following the the "Main.as"
public class Main extends MovieClip { //An array to hold the minutes and seconds in elements [0] and [1] private var timeArray:Array; //Our countTimer private var countTimer:CountTimer; //A boolean that tells us whether this is the first timer //(the one used when the movie first starts) private var firstTimer:Boolean = true; //Direction of our timer can be "up" or "down" private var direction:String; //The MovieClip that contains the text we grow and fade private var growFadeText:GrowFadeText; //The MovieClip that holds our progressBar private var progressContainer:ProgressContainer; //The minutes private var min:int; //The second private var sec:int; public function Main() { min = 1; sec = 0; countTimer = new CountTimer(min,sec,"down",timer_txt); timer_txt.text = "1:00"; countTimer.addEventListener(TimerEvent.TIMER_COMPLETE,timerComplete); setupButtons(); addProgressContainer(0,70,"down"); countTimer.start(); stage.addEventListener(Event.ENTER_FRAME,updateProgress); cancel_btn.addEventListener(MouseEvent.CLICK,moveContainer); }
First we set up the min
and sec
variables and pass them to the countTimer; we then set the direction to "down" and set the target TextField to timer_txt.
We then set some default text for the TextField,add an TIMER_COMPLETE
event to the Timer, run a function to set up our buttons,add the ProgressBar,start the Timer, and add an ENTER_FRAME
event listener to the stage (which we'll use to update the progress bar), and finally add a CLICK
event listener to our cancel_btn
.
Step 13: Coding the timerComplete()
Function
Add the following to "Main.as".
private function timerComplete(e:TimerEvent):void { addGrowFadeText(154,130); if (firstTimer == true) { TweenLite.to(growFadeText, 2.5, {scaleX:2, scaleY:2,alpha:0,onComplete:moveContainer}); } else { TweenLite.to(growFadeText, 2.5, {scaleX:2, scaleY:2,alpha:0,onComplete:showTheControls}); } }
Here we add the text to the stage by calling addGrowFadeText()
we will examine this function shortly.We then check the firstTimer
variable to see if this is the first timer, if it is use tweenLite to grow and fade the text and call the moveContainer
function once the animation completes.If it not the first timer then we once again use tweenLite to grow and fade the text but call the showTheControls()
function once the animation completes.We will examine the moveContainer
and showTheControls
functions shortly.
Step 14: Coding the setupButtons()
Function
Add the following to "Main.as"
private function setupButtons():void { countContainer.countDown_btn.addEventListener(MouseEvent.CLICK,doTimer); countContainer.countUp_btn.addEventListener(MouseEvent.CLICK,doTimer); countContainer.cancel_btn.addEventListener(MouseEvent.CLICK,restoreControls); countContainer.instructions_txt.text = "Enter time in format 1:30 then press CountDown or CountUp button"; //We dont want user to be able to edit the text countContainer.instructions_txt.selectable = false; }
Here we add some EventListeners to our Buttons, set the text for our instructions_txt
and set it so the user cannot select or edit the text. I used a TextField here to keep this already long tutorial shorter, but you would probably want to use a Label component here and style it with a TextFormat Object.
Step 15: Coding the addProgressContainer()
Function
Add the following to "Main.as"
private function addProgressContainer(xPos:int,yPos:int,dir:String):void { progressContainer = new ProgressContainer(); progressContainer.x = xPos; progressContainer.y = yPos; stage.addEventListener(Event.ENTER_FRAME,updateProgress); addChild(progressContainer); if (dir == "down"){ progressContainer.pb.direction = "left"; } else { progressContainer.pb.direction = "right"; } }
Here we create a new progressContainer and set its x
and y
properties by the xPos
and yPos
parameters that are passed in.We then add an ENTER_FRAME
event listener that calls the updateProgress()
function, and we add the progressContainer
to the stage.Finally we check the dir
parameter to see if it is equal to "down"; if it is we set the ProgressBar's direction to "left", otherwise we set the ProgressBar's direction to "right".
Step 16: Coding the removeProgressContainer()
Function
Add the following to "Main.as"
private function removeProgressContainer():void { if (progressContainer != null) { stage.removeEventListener(Event.ENTER_FRAME,updateProgress); removeChild(progressContainer); progressContainer = null; } }
Here we check whether the progressContainer
exists; if it doesn't, then we remove the ENTER_FRAME
event listener from the stage so it does not continue to run.We then remove the progressContainer from the stage and set it to null
since we are done with it.
Step 17: Coding the updateProgress()
Function
Add the following to "Main.as".
private function updateProgress(e:Event):void { progressContainer.progress_txt.text = countTimer.getProg().toString() + "%"; progressContainer.pb.setProgress(countTimer.getTimeLoaded(),countTimer.getTimeTotal()); }
Here we set the progress_txt
's text to show the progress of our timer.We use the countTimer's getProg()
method which returns an integer of what percentage of the time has passed.Since it returns an int we use AS3's built in toString()
method on it,and append a "%" sign to it.
Next we call the setProgress()
method of the ProgressBar component on our ProgressBar (pb
). We pass in countTimer.getTimeLoaded()
and countTimer.getTimeTotal()
which return integers. To learn more about the ProgressBar component check out my Quick Introduction to the ProgressBar component.
Step 18: Coding the addGrowFadeText()
Function
Add the following to "Main.as".
private function addGrowFadeText(xPos:int,yPos:int) { growFadeText = new GrowFadeText(); growFadeText.x = xPos; growFadeText.y = yPos; addChild(growFadeText); }
Here we create a new instance of GrowFadeText, set its x
and y
properties as specified, and then we add it to the stage.
Step 19: Coding the removeGrowFadeText()
Function
Add the following to "Main.as".
private function removeGrowFadeText():void { if (growFadeText != null) { removeChild(growFadeText); growFadeText = null; } }
Here we check to see whether growFadeText
exists. If it doesn't, we remove it from the stage and set it to null.
Step 20: Coding the moveContainer()
Function
Add the folowing to "Main.as".
private function moveContainer(e:Event=null):void { countTimer.stop(); removeChild(timer_txt); removeChild(cancel_btn); removeGrowFadeText(); removeProgressContainer(); countContainer.cancel_btn.visible = false; firstTimer = false; TweenLite.to(countContainer, 1, {x:0}); }
Here we stop the timer and remove the timer_txt
and cancel_btn
. Next we call our removeGrowFadeText()
and removeProgressContainer()
functions, set the cancel_btn
within the countContainer to be invisible, set the firstTimer
variable to false
, and slide the countContainer
in using TweenLite.
Step 21: Coding the showControls()
Function
Add the following to "Main.as":
private function showTheControls():void { showControls(); removeProgressContainer(); countTimer.stop(); countContainer.timer_txt.text = ""; countContainer.timer_txt.text = ""; }
Here we call the showControls
function, which we will examine shortly. Next we call removeProgressContainer()
, stop the timer and reset our TextFields to be blank.
Step 22: Coding the doTimer()
Function
Add the following to "Main.as":
private function doTimer(e:MouseEvent):void { if (e.target == countContainer.countDown_btn) { direction = "down"; } else { direction = "up"; } if (countContainer.timer_txt.text != "") { timeArray = countContainer.timer_txt.text.split(":"); min = timeArray[0]; sec = timeArray[1]; countTimer = new CountTimer(min,sec,direction,countContainer.timer_txt); countTimer.start(); countTimer.addEventListener(TimerEvent.TIMER_COMPLETE,timerComplete); countTimer.addEventListener(TimerEvent.TIMER,updateProgress); hideControls(); addProgressContainer(0,70,direction); } }
We first check to see which button was pressed.If the countDown_btn
was pressed we set the direction
variable to "down" otherwise the we set the direction
variable to "up".Next we check to make sure timer_txt
is not blank.If it is not then we use the split()
function to put the minutes and seconds into the timeArray
.The split()
function takes a string and separates it by whatever you pass it as a parameter -- here we used the colon (:) -- and then adds each "piece" to the array. So, if you passed in "1:45"
, then the array's element [0] would be "1"; element [1] would be "45".
We then set the sec
and min
variables.
Next we create a new instance of the countTimer
and pass in the min
,sec
,dir
,and the TextField to use.
We then add two TimerEvent
listeners that call our timerComplete()
and updateProgress()
functions.
Finally we call the hideControls()
function and add the progressContainer
to the stage.
Step 23: Coding the showControls()
Function
Add the following to "Main.as".
private function showControls():void { countContainer.instructions_txt.visible = true; countContainer.countDown_btn.visible = true; countContainer.countUp_btn.visible = true; countContainer.cancel_btn.visible = false; }
Here we simply set the instruction_txt
,countDown_btn
,and countUp_btn
to be visible. We then set the cancel_btn
to be invisible.
Step 24: Coding the hideControls()
Function
Add the following to "Main.as".
private function hideControls():void { countContainer.instructions_txt.visible = false; countContainer.countDown_btn.visible = false; countContainer.countUp_btn.visible = false; countContainer.cancel_btn.visible = true; }
This is the opposite of the previous function; here we just set the set the instruction_txt
,countDown_btn
,and countUp_btn
to be invisible. We then set the cancel_btn
to be visible.
Step 25: Coding the restoreControls()
Function
Add the following to "Main.as".
private function restoreControls(e:Event) { showTheControls(); countTimer.stop(); }
Here we call the showControls()
function and stop the timer.
Step 26: Close the Class and Package.
Add the following to "Main.as"
}//Close the Class }//Close the Package
Here we close out our class and package.
Step 27: Set the Document Class and Test the Movie
Although not absolutely necessary,set the test
variable to false
in the "CountTimer".as".
Make sure your Document Class is set to "Main" and test the Movie.
Conclusion
We now have a versatile Timer Class that can be used for any number of things, from time limits on tests to setting times for levels on games.
I hope you found this tutorial useful and thanks for reading!
Comments