This isn’t a new idea – these things have been on the web since day one in the form of page counters. This one though takes that same odometer look and animates it via jQuery. What I’ve done is set things up so that the odometer initially appears zero’d out, then it animates up to a predetermined number. From there it starts counting up. The rate at which the counter increments is user-controlled via a drop-down list.

Nothing much to speak of here – it is what it is. I do want to mention though that jQuery’s animate method is instrumental and the “swing” parameter supplies the easing that you will see. Also, as an added point of detail towards achieving a sense of realism you’ll notice that the numbers are at times not necessarily in perfect horizontal alignment. This is on purpose – the sprite sheet has some numbers that are shifted a pixel in some direction. Subtle but something that IMO adds to the overall effect.

Give it a try…

$(window).load(function(){
    var globObj = {bigNumber:'6035909.4'};
    var rotationAmnt = 15;
    var animateSpeed = 3000; 

    var line = [
        {name:'l2_0',counter:0,targetVal:0}, 
	{name:'l2_1',counter:0,targetVal:0},
	{name:'l2_2',counter:0,targetVal:0},
	{name:'l2_3',counter:0,targetVal:0},
	{name:'l2_4',counter:0,targetVal:0},
	{name:'l2_5',counter:0,targetVal:0},
	{name:'l2_6',counter:0,targetVal:0},
	{name:'l2_7',counter:0,targetVal:0}
	];
	
    function doTheNumbers(){
	setTimeout(do_l2_0,200); 
	setTimeout(do_l2_7,400);
	setTimeout(do_l2_6,600);
	setTimeout(do_l2_5,700);
	setTimeout(do_l2_4,1000);
	setTimeout(do_l2_3,1200);
	setTimeout(do_l2_2,1400);
	setTimeout(do_l2_1,1600);
    }
	
    function do_l2_0(){rotateNumbers(0);}
    function do_l2_1(){rotateNumbers(1);}
    function do_l2_2(){rotateNumbers(2);}
    function do_l2_3(){rotateNumbers(3);}
    function do_l2_4(){rotateNumbers(4);}
    function do_l2_5(){rotateNumbers(5);}
    function do_l2_6(){rotateNumbers(6);}
    function do_l2_7(){rotateNumbers(7);}
	
    function rotateNumbers(arrNum){
	$('#'+line[arrNum].name).css('top','0');
	$('#'+line[arrNum].name).animate({top:'-360'},100,'linear',function(){
	    if (line[arrNum].counter == rotationAmnt){
		var targ = (-(line[arrNum].targetVal*40)).toString();
		$('#'+line[arrNum].name).animate({top:targ},1000,'swing',function(){
		    if (arrNum == line.length-1 && line[line.length-1].counter == rotationAmnt){
			setTimeout(animationsComplete,2000);
		    }
		});
	    } else {
		line[arrNum].counter = line[arrNum].counter + 1;
		rotateNumbers(arrNum,line);
	    }
	});
    }
	
    function animationsComplete(){
	incrementTenth();
    }
	
    function getRealNumber(str){
	var adj = str.substr(str.indexOf('-')+1,str.indexOf('px')-1);
	adj = adj == '' ? 0 : adj;
	return adj;
    }
	
    function incrementTenth(){
	var current = $('#l2_0').css('top');
	$('#l2_0').animate({top:'-=40'},animateSpeed,'swing',function(){
	    var newTopNum = getRealNumber($('#l2_0').css('top'));
	    if (newTopNum == 360){
		incrementWhole(0);
	    } else if (newTopNum == 400){
		$('#l2_0').css('top',0);
	    }
	    incrementTenth();
	});
    }
	
    function incrementWhole(n){
	var d = ['#l2_7','#l2_6','#l2_5','#l2_4','#l2_3','#l2_2','#l2_1'];
	if (getRealNumber($(d[n]).css('top')) == 360){
	    incrementWhole(n+1);
	}
	$(d[n]).animate({top:'-=40'},animateSpeed,'swing',function(){
	    var newTopNum = getRealNumber($(d[n]).css('top'));
	    if (newTopNum == 400){
		$(d[n]).css('top',0);
	    }
	});
    }

    ;(function getObjVals(){
	var wholeNumbers,decimalNumbers;
	if (globObj.bigNumber.toString().indexOf('.') != -1){
		var temp = globObj.bigNumber.split('.');
		wholeNumbers = temp[0];
		decimalNumbers = temp[1];
	} else {
		wholeNumbers = globObj.bigNumber;
	}
	for (var i=1;i<line.length;i++){
		line[i].targetVal = parseInt(wholeNumbers.substr(i-1,1));
	}
	line[0].targetVal = decimalNumbers ? parseInt(decimalNumbers.substr(0,1)) : 0;
	doTheNumbers();
    })();

    $.extend({
	changeSpeed:function(n){
	    if (!isNaN(n))animateSpeed = parseInt(n);
	}
    });
});