Posted 09/27/2006 in flash

So it's key in any application to keep the user informed and it can be a huge disconnect if the user doesn't know if they should interact or wait. Such is the case with many applications - where the user doesn't know if the program is "thinking" and if they should wait. Hence the simple elegance of the animated "busy" icon present in every popular operating system. Hence the need to add it to my growing list of custom components.

With custom ecommerce flash RIA's I see an immediate need for a generic and non-instrusive "please wait" animation that essentially keeps the user in sync with the application. This is especially necessary considering the amount of remoting involved with every action made in an ecommerce application. This standard was literally set since the first "throbber" in the NCSA Mosiac program with the ugly "N" graphic moving in and out. Very non-exciting - but it got it's job done and rather well for that matter.

I tried to put a couple test animations together using purely AS2.0. I've found that lately the FireFox "throbber" animation has a good adoption for numerous flash RIAs and plain animations as well as fancy pre-loaders. I essentially wanted a lightweight piece of AS2.0 code that could render such an animation... The code below draws either a circle or a bar in a circular manner and uses the Tween class change the opacity of each leaf node in a circular fashion - just like the FireFox throbber.

New way of doing it

I tried to manifest this idea through tween running for 1000millisecond which were started on a 100millisecond interval if there were 10 leaf nodes and relying on Tween::onMontionFinished() to precisely loop the animation. This is impossible. now... on to purely using a setInterval without any Tween madness so that the only affect a few millisecond slippage will have is if the viewer's eyes are quick enough to notice it (highly unlikely considering we can't notice the difference at 33.3milliseconds (NTSC 30 frames per second format)).

Here's the much better manifestation - that doesn't "bog" down or get out of sync:

and for the new code:
class Throbber extends MovieClip {
	private var _leafSize:Number = 4;
	private var _cycleSpeed:Number = 1000;
	private var _color:Number = 0x808080;
	private var _leafCount:Number = 12;
	private var _trailCount:Number = 5;
	private var _circleRadius:Number = 10;
	private var _startLeaf:Number = 9;
	private var _padding:Number = 10;
	private var throbber:MovieClip;
	private var leafNodeItr:Number = 1;
	private var interval;
	// no constructor
	function init(width:Number, height:Number, Txt:String) {
		this.throbber = this.createEmptyMovieClip("throbber", this.getNextHighestDepth());
		this.throbber._x = this._circleRadius + this._padding;
		this.throbber._y = 0;
		var offsetMilliseconds = new Date().getMilliseconds();
		var offsetLeafCnt:Number;
		for (var i=this._startLeaf; i < (this._leafCount + this._startLeaf); i++) {
			offsetLeafCnt = i - this._startLeaf + 1;
			this.throbber["leaf" + offsetLeafCnt] = this.throbber.createEmptyMovieClip("leaf" + offsetLeafCnt, this.throbber.getNextHighestDepth());
			//this.drawLeafCircle(this.throbber["leaf" + offsetLeafCnt], (Math.cos((i * (360 / this._leafCount)) * (3.14/180)) * this._circleRadius), (Math.sin((i * (360 / this._leafCount)) * (3.14/180)) * this._circleRadius));
			this.drawLeafStub(this.throbber["leaf" + offsetLeafCnt], (Math.cos((i * (360 / this._leafCount)) * (3.14/180)) * this._circleRadius), (Math.sin((i * (360 / this._leafCount)) * (3.14/180)) * this._circleRadius));
			this.throbber["leaf" + offsetLeafCnt]._alpha = 25;
		this.interval = setInterval(
				function () { 
					if (this.leafNodeItr > this._leafCount)
						this.leafNodeItr = 1;
					var preLo = (this._trailCount > this.leafNodeItr) ? 0 : (this.leafNodeItr - this._trailCount);
					var preHi = this.leafNodeItr;
					var apreLo = (this._trailCount > this.leafNodeItr) ? (this._leafCount - (this._trailCount - this.leafNodeItr)) : this._leafCount;
					var apreHi = this._leafCount;
					var fadeCount:Number = 25;
					for (var i:Number = 1; i <= this._leafCount; i++) {
						if (i == this.leafNodeItr) {
							this.throbber["leaf" + i]._alpha = 100;
						} else if ( i > preLo && i <= preHi ) {
							this.throbber["leaf" + i]._alpha = 25 + (Math.round(75 / this._trailCount) * (i - preLo - (apreLo - this._leafCount)));
						} else if ( i > apreLo && i <= apreHi ) {
							this.throbber["leaf" + i]._alpha = 25 + (Math.round(75 / this._trailCount) * (i - (this._leafCount - (preLo - (apreLo - this._leafCount)))));
						} else {
							this.throbber["leaf" + i]._alpha = 25;
			Math.round(this._cycleSpeed / this._leafCount));
	function drawLeafStub (target:MovieClip, x:Number, y:Number) {
		target.lineStyle(this._leafSize, this._color, 100);
		target.moveTo(x, y);
		target.lineTo(x*1.5, y*1.5);
	function drawLeafCircle (target:MovieClip, x:Number, y:Number) {
		var w = this._leafSize;
		var h = this._leafSize;
		// center the circle
		w /= 2; h /= 2;
		x += w; y += h;
		var xc1 = w*(Math.SQRT2-1), xc2 = w*(Math.SQRT2/2);
		var yc1 = h*(Math.SQRT2-1), yc2 = h*(Math.SQRT2/2);
		target.lineStyle(0, this._color, 100);
		target.moveTo(x+w, y);
		target.curveTo(x+w, y+yc1, x+xc2, y+yc2);
		target.curveTo(x+xc1, y+h, x, y+h);
		target.curveTo(x-xc1, y+h, x-xc2, y+yc2);
		target.curveTo(x-w, y+yc1, x-w, y);
		target.curveTo(x-w, y-yc1, x-xc2, y-yc2);
		target.curveTo(x-xc1, y-h, x, y-h);
		target.curveTo(x+xc1, y-h, x+xc2, y-yc2);
		target.curveTo(x+w, y-yc1, x+w, y);
	function _close () {

This is the wrong way to do it (this is the old flawed code I first posted)
I found quickly that this throbber doesn't work well outside of the flash IDE seeing as there's no way to rely on internal tween setinterval timing. I quickly found that the setInterval is anything but precise in terms of timing events in Milliseconds. This is especially true in the browser flash plugin.

I'm keeping this code here for the sake of pointing out the trial-by-fire learning curve. Such event timing is always an issue hence relying on it for precise animations on the Milliseconds level is just a no-go.

Bar (aka "stub") leaf node example:

Circle leaf node example:

Here's some uncommented code from the initial version: throbber-alpha-snippet
new comment
EMAIL (hidden)
MESSAGE TAGS ALLOWED: <code> <a> <pre class="code [tab4|tabX|inline|bash]"> <br>