1
votes

I have been looking for an example code to create a simple countdown timer .

It would display the time in "Hours : Minutes : Seconds" and I would have the ability to set the time to 10 min, 30 min, 1 hour, 2 hour , etc.

When the timer hits zero, it would do a function. Your help is greatly appreciated. The only stuff I could find online was for counting down to a specific date.

2

2 Answers

1
votes

Making a timer is very simple. You can read more on the AS3 Timer Class. Displaying the time as HH:MM:SS is a little bit of work. I use this class when I need to do that:

package com.dop.utils
{
    public class Timecodes
    {
        public function Timecodes()
        {
        }

        public static function timecodeToSeconds(tcStr:String):Number
        {
            var t:Array = tcStr.split(":");
            return (t[0] * 3600 + t[1] * 60 + t[2] * 1);
        }

        public static function secondsToTimecode(seconds:Number):String
        {
            var minutes:Number          = Math.floor(seconds/60);
            var remainingSec:Number     = seconds % 60;
            var remainingMinutes:Number = minutes % 60;
            var hours:Number            = Math.floor(minutes/60);
            var floatSeconds:Number     = Math.floor((remainingSec - Math.floor(remainingSec))*100);
            remainingSec                = Math.floor(remainingSec);

            return getTwoDigits(hours) + ":" + getTwoDigits(remainingMinutes) + ":" + getTwoDigits(remainingSec);
        }

        private static function getTwoDigits(number:Number):String
        {
            if (number < 10)
            {
                return "0" + number;
            }
            else
            {
                return number + "";
            }
        }
    }
}

I made a little example that you can see here using this class: http://ronnieswietek.com/cc/alarm/alarm.swf

(and source here: http://ronnieswietek.com/cc/alarm/alarm.fla)

The code I used that utilizes the Timer class is here:

import com.dop.utils.Timecodes;
import flash.events.*;
import fl.controls.*;
import fl.data.*;
import flash.utils.Timer;

var timer:Timer = new Timer(1000); //-- run once a second
timer.addEventListener(TimerEvent.TIMER, onTimer);

var countdown:Number = 0;
var durations:Array = [
    {label:'1 minute',time:1},
    {label:'5 minutes',time:5},
    {label:'10 minutes',time:10},
    {label:'30 minutes',time:30},
    {label:'1 hour',time:60},
    {label:'2 hours',time:120},
    {label:'3 hours',time:180}
];

durationBox.dataProvider = new DataProvider(durations);
timerButton.addEventListener(MouseEvent.CLICK, timerHandler);

function timerHandler(e:MouseEvent):void
{
    if (!timer.running)
    {
        var selectedTime:Number = durationBox.selectedItem.time * 60;
        countdown = selectedTime;
        timeText.text = Timecodes.secondsToTimecode(countdown);
        timer.start();
        timerButton.label = "Stop";
    }
    else
    {
        countdown = 0;
        timeText.text = Timecodes.secondsToTimecode(countdown);
        timer.stop();
        timer.reset();
        timerButton.label = "Start";
    }
}

function onTimer(e:TimerEvent):void
{
    timeText.text = Timecodes.secondsToTimecode(countdown);
    countdown--;
    if (countdown == 0)
    {
        timer.stop();
        timer.reset();
        timeText.text = "ALARM!!!";
    }
}
1
votes

Where accuracy is required you can't rely on the Timer class alone to give you a precise passage of time. Especially if the Flash Player is doing a lot of other work. This is because the Timer classes interval is actually a request and not a guarantee. Let's take a look, start with a trivial implementation of a timer ticking once a second (1000 milliseconds):

private var startTime:uint;
private var ticks:int;

protected function start():void
{
    var t:Timer = new Timer( 1000 );
    t.addEventListener( TimerEvent.TIMER, timerTick );
    t.start();
    startTime = getTimer();
}

protected function timerTick( event:TimerEvent ):void
{
    trace( 'Ideal: ' + (++ticks) + ' Actual: ' + (getTimer()-startTime)/1000 );
}

Using getTimer (get familiar with getTimer) to measure the actual time we can see within 20 seconds the Timer class is a half a second behind. This drift will vary each time this is run:

Expected: 1 Actual: 1.043
Expected: 2 Actual: 2.083
Expected: 3 Actual: 3.082
…
Expected: 18 Actual: 18.417
Expected: 19 Actual: 19.457
Expected: 20 Actual: 20.5

That's where implementing a Stopwatch comes in handy for measuring time more precisely:

import flash.utils.getTimer;

public class Stopwatch
{
    private var startStamp:Number;
    private var stopStamp:Number;
    private var runTime:Number;

    private var _countdownDuration:Number;

    private var started:Boolean;
    private var stopped:Boolean;
    private var paused:Boolean;

    function Stopwatch( startNow:Boolean = true ):void
    {
        if ( startNow ) 
            start();
    }

    public function start():void
    {
        runTime = 0;
        startStamp = getTimer();
        _countdownDuration = 0;
        started = true;
        stopped = false;
        paused = false;
    }

    public function startCountdown( milliseconds:Number ):void
    {
        start();
        _countdownDuration = milliseconds;
    }

    public function pause():void
    {
        if ( started && ! stopped )
        {
            runTime += getTimer() - startStamp;
            paused = true;
        }
    }

    public function resume():void
    {
        if ( started && paused )
        {
            startStamp = getTimer();
            paused = false;
        }
    }

    public function stop():void
    {
        if ( started && ! stopped )
        {
            if ( ! paused )
                runTime += getTimer() - startStamp;

            stopped = true;
            paused = false;
        }
    }

    public function set elapsed( value:uint ):void
    {
        runTime = value;

        if ( running )
            startStamp = getTimer();
    }

    public function get elapsed():uint
    {
        if ( running )
            return ( getTimer() - startStamp ) + runTime;

        return runTime;
    }

    public function get running():Boolean
    {
        return ( started && ! paused && ! stopped );
    }

    public function get countdownDuration():Number
    {
        return _countdownDuration;
    }

    public function set countdownDuration( value:Number ):void
    {
        _countdownDuration = value;
    }

    public function get remaining():int
    {
        if ( ! _countdownDuration )
            return 0;
        else if ( _countdownDuration - elapsed < 0 )
            return 0;

        return _countdownDuration - elapsed;
    }
}

Extending the first example with Stopwatch you can effectively measure the passage of time very simply (just remember stopwatch.elapsed is in milliseconds so we'll divide by 1000 for seconds):

private var stopwatch:Stopwatch;

protected function start():void
{
    var t:Timer = new Timer( 1000 );
    t.addEventListener( TimerEvent.TIMER, timerTick );
    t.start();
    stopwatch = new Stopwatch;
    stopwatch.start();
}

protected function timerTick( event:TimerEvent ):void
{
    trace( stopwatch.elapsed/1000 + ' seconds have elapsed',
           (60 * 10) - stopwatch.elapsed/1000 + ' seconds remain' );
}

Since stopwatch.elapsed is in milliseconds, you'll want to convert that quantity to different time increments. Following the Single Responsibility Principle we'll make a reusable general use class called StopwatchFormatter to help us consolidate these calculations and expose a readable API:

public class StopwatchFormatter
{
    private var elapsed:Number;
    public var paddedSize:int;
    public var cappedDecimalLength:int;

    function StopwatchFormatter( paddedSize:Number = 2, cappedDecimalLength:Number = 1,  elapsed:Number = 0 )
    {
        this.elapsed = elapsed;
        this.paddedSize = paddedSize;
        this.cappedDecimalLength = cappedDecimalLength;
    }

    //  INPUTS

    public function setTimeAsGroup( hours:Number, minutes:Number = 0, seconds:Number = 0, milliseconds:Number = 0 ):StopwatchFormatter
    {
        elapsed = ( hours * 60 * 60 * 1000 ) + ( minutes * 60 * 1000 ) + ( seconds * 1000 ) + milliseconds;

        return this;
    }

    public function set totalMilliseconds( value:Number ):void
    {
        elapsed = value;
    }

    public function set totalSeconds( value:Number ):void
    {
        elapsed = value * 1000;
    }

    public function set totalMinutes( value:Number ):void
    {
        elapsed = value * 1000 * 60;
    }

    public function set totalHours( value:Number ):void
    {
        elapsed = value * 1000 * 60 * 60;
    }

    //  CLOCK LIKE 
    //  (converting to int will drop the decimal place) 

    public function get milliseconds():int
    {
        return elapsed % 1000;
    }

    public function get seconds():int
    {
        return ( elapsed / 1000 ) % 60;
    }

    public function get minutes():int
    {
        return ( elapsed / 1000 / 60 ) % 60;
    }

    public function get hours():int
    {
        return ( elapsed / 1000 / 60 / 60 ) % 24;
    }

    // CLOCK PADDED (zeroes in the front) 
    // 5 becomes "05" , 10 becomes "10" where _paddedSize is 2

    public function get millisecondsPadded():String
    {
        return frontPad( milliseconds );
    }

    public function get secondsPadded():String
    {
        return frontPad( seconds );
    }

    public function get minutesPadded():String
    {
        return frontPad( minutes );
    }

    public function get hoursPadded():String
    {
        return frontPad( hours );
    }

    // TOTAL

    public function get totalMilliseconds():Number
    { 
        return elapsed;
    }

    public function get totalSeconds():Number
    {
        return elapsed  / 1000;
    }

    public function get totalMinutes():Number
    {
        return elapsed / 1000 / 60;
    }

    public function get totalHours():Number
    {
        return elapsed / 1000 / 60 / 60;
    }

    //  TOTAL CAPPED
    //  3.134 becomes 3.1 where _cappedDecimalLength is 1

    public function get totalMillisecondsCapped():Number
    {
        return capped( totalMilliseconds );
    }

    public function get totalSecondsCapped():Number
    {
        return capped( totalSeconds );
    }

    public function get totalMinutesCapped():Number
    {
        return capped( totalMinutes );
    }

    public function get totalHoursCapped():Number
    {
        return capped( totalHours );
    }

    // TOTAL CAPPED + PADDED (zeroes in the back and one zero in the front for values less than 0)
    //  3.101 becomes "3.10" where _cappedDecimalLength is 2

    public function get totalSecondsCappedPadded():String
    {
        return capped( totalSeconds ).toFixed( cappedDecimalLength );
    }

    public function get totalMinutesCappedPadded():String
    {
        return capped( totalMinutes ).toFixed( cappedDecimalLength );
    }

    public function get totalHoursCappedPadded():String
    {
        return capped( totalHours ).toFixed( cappedDecimalLength );
    }

    // UTILITY FUNCTIONS

    private function frontPad( n:int ):String
    {
        var s:String = n.toString();
        if ( s.length < paddedSize )
        {
            var i:int = 0;
            var len:int = paddedSize - s.length;
            for ( ; i < len; i++ )
            {
                s = "0" + s;
            }
        }
        return s;
    }

    private function capped( input:Number ):Number
    {
        if ( cappedDecimalLength == 0 ) 
            return Math.floor( input );

        var decimalFactor:Number = Math.pow( 10, cappedDecimalLength );

        return Math.floor( input * decimalFactor ) / decimalFactor;
    }
}

Pulling it all together with these two classes and a Timer we have a trivial way to countdown:

private var stopwatch:Stopwatch;
private var time:StopwatchFormatter;

protected function start():void
{
    var t:Timer = new Timer( 1000 );
    t.addEventListener( TimerEvent.TIMER, timerTick );
    t.start();
    stopwatch = new Stopwatch;
    stopwatch.startCountdown( new StopwatchFormatter().setTimeAsGroup( 1, 10, 30 ).totalMilliseconds );
    time = new StopwatchFormatter;
}

protected function timerTick( event:TimerEvent ):void
{
    time.totalMilliseconds = stopwatch.elapsed;
    var elapsed:String = time.hoursPadded + ':' + time.minutesPadded + ':' + time.secondsPadded + ':' + time.millisecondsPadded;

    time.totalMilliseconds = stopwatch.remaining;
    var remainig:String = time.hoursPadded + ':' + time.minutesPadded + ':' + time.secondsPadded + ':' + time.millisecondsPadded;

    trace( 'Elapsed:', elapsed, "Remaining:", remainig ); 
}