Skip to content

Instantly share code, notes, and snippets.

@elrasguno
Created April 13, 2011 22:27
Show Gist options
  • Save elrasguno/918566 to your computer and use it in GitHub Desktop.
Save elrasguno/918566 to your computer and use it in GitHub Desktop.
Convert a container that normally holds a number into an "odometer" style collection of animated divs.
(function($) {
$.extend($.fn, {
animateDiv : function(rValue, rOptions, rSpriteOrImg) {
var selff = this,
debug = false,
vFrames = rOptions['pFrames'] || 10,
vFrameSize = rOptions['pFrameSize'],
vOrientation = (rOptions['pOrientation'] > -1 ? rOptions['pOrientation'] : 1),
vClassName = rOptions['pClassName'] || 'numbers_test',
vSpriteOrImg = rSpriteOrImg || 'sprite';
/**************************************************************
** UTILITY FUNCTIONS
**************************************************************/
/***
Function used to animate to next frame
***/
function animateSprite($rObj, rDirection){
var vPosition,
vPositionSplit,
vPositionX,
vPositionY;
if(!$rObj || ($rObj && !$rObj.css)) return;
// rDirection could move frames backwards if set.
rDirection = rDirection || 1;
// IE fix when background position isn't officially set.
vPosition = $rObj.css('backgroundPosition') || '0px 0px';
if(!vPosition) return;
vPositionSplit = vPosition.split(' ');
vPositionX = vPositionSplit[0],
vPositionY = vPositionSplit[1];
if(vSpriteOrImg === 'sprite') {
$rObj.css('backgroundPosition', (vOrientation
/* vertical */ ? (rDirection && rDirection > 0
/* forwards */ ? vPositionX + ' ' + (-vFrameSize * ($rObj.pFrame = $rObj.pFrame == vFrames-1 ? 0 : $rObj.pFrame + rDirection)) + 'px'
/* backwards */ : vPositionX + ' ' + (-vFrameSize * ($rObj.pFrame = $rObj.pFrame == 0 ? vFrames-1 : $rObj.pFrame + rDirection)) + 'px')
/* horizontal */ : (rDirection && rDirection > 0
/* forwards */ ? (-vFrameSize * ($rObj.pFrame = $rObj.pFrame == vFrames-1 ? 0 : $rObj.pFrame + rDirection)) + 'px ' + vPositionY
/* backwards */ : (-vFrameSize * ($rObj.pFrame = $rObj.pFrame == 0 ? vFrames-1 : $rObj.pFrame + rDirection)) + 'px ' + vPositionY)));
}
if(vSpriteOrImg === 'image') {
$rObj.find('img').css((vOrientation ? 'top' : 'left'), (vOrientation
/* vertical */ ? (rDirection && rDirection > 0
/* forwards */ ? (-vFrameSize * ($rObj.pFrame = $rObj.pFrame == vFrames-1 ? 0 : $rObj.pFrame + rDirection)) + 'px'
/* backwards */ : (-vFrameSize * ($rObj.pFrame = $rObj.pFrame == 0 ? vFrames-1 : $rObj.pFrame + rDirection)) + 'px')
/* horizontal */ : (rDirection && rDirection > 0
/* forwards */ ? (-vFrameSize * ($rObj.pFrame = $rObj.pFrame == vFrames-1 ? 0 : $rObj.pFrame + rDirection)) + 'px '
/* backwards */ : (-vFrameSize * ($rObj.pFrame = $rObj.pFrame == 0 ? vFrames-1 : $rObj.pFrame + rDirection)) + 'px ')));
// To address FF bug, set non-affected property to 0.
$rObj.find('img').css((vOrientation ? 'left' : 'top'), '0px');
}
// Stop at a specific pFrame after restarting animation.
if($rObj.pLastFrame >= 0 && $rObj.pFrame === $rObj.pLastFrame) {
$rObj.stopAnimation();
}
}
/***
Take full value and split it into array of individual values to become
separate sprite animations.
***/
function splitValues(rValue) {
var a = [],
vValue = rValue;
// Split digits of value into array. Example: "807" becomes [8, 0, 7];
$.each(rValue.toString().split(''), function() { a.push(parseInt(this, 10)); });
return a;
}
/***
format the value into an array of displayable cells, including possibly a unit suffix, K, M, B, T
return an object with the cells and an index to the unit. the index is set to -1 if the unit is not necessary.
***/
function formatValues(rValue) {
var cells = splitValues(rValue);
var units = ['K','M','B','T'];
var unitIdx = -1;
while ( cells.length > 6 ) {
unitIdx ++;
cells = cells.slice(0, cells.length - 3);
if ( unitIdx == units.length - 1 ) {
break;
}
}
if ( unitIdx >= 0 ) {
cells.push(units[unitIdx]);
}
return {
cells : cells,
unit : unitIdx
}
}
/***
Extend given node to have startAnimation, stopAnimation.
***/
function extendNode(rNode, rLastFrame)
{
return $.extend(rNode, {
pFrame : 0,
pLastFrame : rLastFrame,
startAnimation : function(interval, rDirection) {
this.stopAnimation();
var selff = this;
this.vTimer = setInterval(function() {
animateSprite.call(null, selff, rDirection);
}, interval);
return this;
},
stopAnimation : function() {
if ( this.vTimer != undefined ) {
clearInterval(this.vTimer);
this.vTimer = undefined;
}
return this;
}
});
}
/**************************************************************
** END UTILITY FUNCTIONS
**************************************************************/
/***
If not passed an rValue parameter, simply return an animatable version of the node.
***/
if(rValue == undefined || rValue === null) {
return extendNode(this);
}
/***
sanity check!
***/
rValue = Math.max(0, rValue);
var fSplitValues = formatValues(rValue);
vSplitValues = fSplitValues.cells;
var unitIdx = fSplitValues.unit;
var vNodes = (function() {
var a = [],
vNode,
vNumbersContainer = selff.html('');
/***
Iterate backwards through numbers of value, unshifting nodes
onto the nodes list.
***/
for(var vValues = vSplitValues, vIdx = vValues.length - 1; vIdx >= 0 ; vIdx--) {
// Differentiate between sprite divs or images
vNode = (vSpriteOrImg === 'sprite'
? $('<div>').addClass((typeof vSplitValues[vIdx] === 'number' ? vClassName : 'numbers_suffix'))
: $('<div>').addClass('animated_image').html(
$('<img>').attr({'src': '/app/weeworldrpg/graphics/sprite_numbers_small.png', 'width': '12', 'height': '194'})
.css('position', 'absolute')
.css('left', '0')
.css('top' , '0')));
if (vSpriteOrImg === 'sprite' && (typeof vSplitValues[vIdx] !== 'number') && unitIdx >= 0 ) {
vNode.css('backgroundPosition', '0px ' + (-vFrameSize * unitIdx) + 'px');
}
a.unshift(vNode);
vNumbersContainer.prepend(vNode);
}
return a;
})();
debug && debugLog('vNodes', vNodes);
for(var vValues = vSplitValues, vIdx = vValues.length - 1; vIdx >= 0 ; vIdx--)
{
debug && debugLog('vNode?', vSpriteOrImg, vNodes[vIdx]);
if (typeof vValues[vIdx] === 'number') {
vNodes[vIdx] = extendNode(vNodes[vIdx], vValues[vIdx]);
}
}
return $.extend(this, {
'pNodes' : vNodes,
'pValue' : rValue,
updateNodes : function(rValue)
{
rValue = Math.max(0, rValue);
var fOrigValue = formatValues(this.pValue),
fNewValue = formatValues(rValue);
// original and new values can only be compared apple to apple after the formatting by taking
// units into consideration
var vOrigValue = fOrigValue.cells,
vNewValue = fNewValue.cells,
vSplitValues = vNewValue;
var vNode,
vNumDigitsChanged = vOrigValue.length !== vNewValue.length,
vNumDigitsDelta = (vNumDigitsChanged && Math.abs(vOrigValue.length - vNewValue.length)) || 1;
/***
Account for adding or subtracting numbers if necessary.
If number of digits increases, unshift onto the nodes array.
Otherwise, shift off pNodes array.
***/
if(vNumDigitsChanged)
{
debug && debugLog('vNumDigitsChanged', this);
debug && debugLog('vNumDigitsDelta', vNumDigitsDelta);
debug && debugLog('vOrigValue', vOrigValue, 'vNewValue', vNewValue);
/***
This loop ensures that list of nodes will be adjusted properly
when the change in number of digits is > 1 i.e. from 100 to 0
or vice-versa.
***/
for(var vIdx = 0; vIdx < vNumDigitsDelta; vIdx++) {
if(vOrigValue.length < vNewValue.length)
{
// Differentiate between sprite divs or images
vNode = (vSpriteOrImg === 'sprite'
? $('<div>').addClass(vClassName)
: $('<div>').addClass('animated_image').html(
$('<img>').attr({'src': '/app/weeworldrpg/graphics/sprite_numbers_small.png', 'width': '12', 'height': '194'})
.css('position', 'absolute')
.css('left', '0')
.css('top' , '0')));
this.pNodes.unshift(vNode);
this.prepend(extendNode(vNode));
} else {
$(this.find('div')[0]).remove();
this.pNodes.shift();
}
}
}
this.pValue = rValue;
/***
this new value is different from the old 'order of magnitude'
***/
if ( fOrigValue.unit != fNewValue.unit ) {
// todo: image animation type
if (vSpriteOrImg === 'sprite' ) {
this.pNodes[this.pNodes.length-1].removeClass();
if ( fNewValue.unit >= 0 ) {
this.pNodes[this.pNodes.length-1].css('backgroundPosition', '0px ' + ( - vFrameSize * fNewValue.unit ) + 'px');
this.pNodes[this.pNodes.length-1].addClass('numbers_suffix');
}
else {
this.pNodes[this.pNodes.length-1].addClass(vClassName);
}
}
}
$.each(this.pNodes, function(k) {
this.pLastFrame = vSplitValues[k];
/***
Compare this.pFrame to new value from vSplitValues.
If they're equal, do nothing, otherwise, animate node.
***/
if(typeof this.pLastFrame === 'number') {
if ( this.pFrame !== this.pLastFrame && this.startAnimation) {
this.startAnimation(100, (this.pFrame < this.pLastFrame ? 1 : -1));
}
}
else {
// reset in case a Unit cell was used last round
this.pFrame = 0;
}
});
return this;
}
});
}
});
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment