Created
April 13, 2011 22:27
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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