From: dsc Date: Wed, 8 Dec 2010 08:07:13 +0000 (-0800) Subject: Checkpoint on animation. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=7de7b43da269585c2ad669507c5933ecc54c75a0;p=tanks.git Checkpoint on animation. --- diff --git a/bin/deploy.sh b/bin/deploy.sh index be07bb0..7452616 100755 --- a/bin/deploy.sh +++ b/bin/deploy.sh @@ -1,5 +1,11 @@ #! /bin/bash +VERBOSE="" +SRC="src/Y src/ezl src/tanks" +EXCLUDE="--exclude=$(join ' --exclude=' 'tmp' 'src' 'bin' $*)" +GIT_VERSION=$(git show --oneline . | head -n1 | cut -d ' ' -f 1) + + function halp () { cat >&2 <<-HALP The Littlest Deployer of Battletanks @@ -20,35 +26,33 @@ TODO: Optionally minify using Google Closure Compiler. Options: -h Displays this help - -v Verbose logging + -v Verbose -n Dry-run -C Clean before rebuilding - -G Only emit the deploy version (Git commit abbreviation) + -G Only emit the deploy version (Git commit abbreviation: $GIT_VERSION) HALP } SHIFT=0 function incshift () { SHIFT=$(( $SHIFT + ${1:-1} )); } function fail () { echo; echo "PREDICTABLE FAILURE. $1" >&2; exit 1; } -function join () { sep="$1"; out="$2"; shift 2; for a in $*; do out="${out}${sep}${a}"; done; echo "$out"; } +function join () { sep="$1"; printf "$2"; shift 2; for a in $*; do printf "$sep$a"; done; echo; } +function log () { test "$VERBOSE" && echo && echo "$*"; } for opt in $*; do echo $opt | egrep -xq -e '--?h(e(lp?)?)?' && { halp; exit 0; } done -while getopts "nvCG" opt; do +while getopts ":vnCG" opt; do case $opt in - n ) DRY_RUN="--dry-run"; incshift ;; v ) VERBOSE="-v"; incshift ;; + n ) DRY_RUN="--dry-run"; incshift ;; C ) CLEAN="--clean"; incshift ;; G ) GIT_VERSION_ONLY=1; incshift ;; + * ) fail "Unknown option: $OPTARG" ;; esac done shift $SHIFT -SRC="src/Y src/ezl src/tanks" -EXCLUDE="--exclude=$(join ' --exclude=' 'tmp' 'src' 'bin' $*)" -GIT_VERSION=$(git show --oneline . | head -n1 | cut -d ' ' -f 1) - if test "$GIT_VERSION_ONLY"; then echo "$GIT_VERSION" exit diff --git a/src/ezl/loop/animation.cjs b/src/ezl/loop/animation.cjs new file mode 100644 index 0000000..46589ea --- /dev/null +++ b/src/ezl/loop/animation.cjs @@ -0,0 +1,168 @@ +var Y = require('Y').Y +, evt = require('evt') +, + +/** + * All easing functions adhere to the following interface. + * + * @param {Float} p Proportion completed [0-1]. + * @param {Number} elapsed Miliseconds elapsed since start. + * @param {Number} start Starting value. + * @param {Number} span Delta value (finish - start) to animate. + * @param {Number} duration Total duration in miliseconds of the animation. + */ +Easing = +exports['Easing'] = { + 'linear' : function linearEasing( p, elapsed, start, span, duration ) { + return start + span * p; + }, + + 'swing' : function swingEasing( p, elapsed, start, span, duration ) { + return ((-Math.cos(p*Math.PI)/2) + 0.5) * span + start; + } +} +, + + +lookupEasing = +exports['lookupEasing'] = +function lookupEasing(name, prop){ + if ( Y.isFunction(name) ) + return name; + if (name in Easing) + return Easing[name]; + if ( !(Y.isPlainObject(name) && prop in name) ) + throw new Error('Unknown easing function '+name+'!'); + return lookupEasing(name[prop]); +} +, + + +Fx = +exports['Fx'] = +Y.subclass('Fx', function setupFx(Fx){ + var VAL_PATTERN = /^([+\-]=)?([\d+.\-]+)(.*)$/; + + this['init'] = + function initFx(obj, duration, prop, val, easing){ + var parts = VAL_PATTERN.exec(val) + , rel = parts[1] + , val = parseFloat(parts[2]) + , unit = parts[3] || null + ; + + this.obj = obj; + this.duration = duration; + this.prop = prop; + this.unit = unit; + this.easing = Easing[easing]; + + this.start = this.cur = Y.attr(obj, prop); + if (rel) { + this.span = val*(rel === '-=' ? -1 : 1); + this.finish = this.start + this.span; + } else { + this.span = val - this.start; + this.finish = val; + } + }; + + this['start'] = + function start(now){ + if (this.running) return; + this.startTime = now || new Date().getTime(); + this.running = true; + }; + + this['stop'] = + function stop(){ + this.running = false; + }; + + this['tick'] = + function tick(elapsed, now){ + if (!this.running) + return false; + + var p = (now - this.startTime) / this.duration + , v = this.cur = this.start + this.easing(p, 0, this.span, this.duration) ; + Y.attr(this.obj, this.prop, ( this.unit !== null ? v+this.unit : v )); + + return true; + }; +}) +, + +Animation = +exports['Animation'] = +new evt.Class('Animation', function setupAnimation(Animation){ + Y.extend(this, { + 'started': 0, + 'running': false + }); + + var defOptions = { + 'delay' : 0, + // 'queue' : undefined, + 'easing' : 'linear' + // 'complete' : null, + // 'step' : null + }; + + /** + * @param {Object} obj Object to animate. + * @param {Number} duration Duration (ms) of animation. + * @param {Object} props Object whose keys are the property-names to animate + * paired with the target value. Animated properties can also be relative. + * If a value is supplied with a leading += or -= sequence of characters, + * then the target value is computed by adding or subtracting the given + * number from the current value of the property. + * @param {Object} [options] Animation options: + * * {Number} [delay=0] Milliseconds to wait before beginning animation. + * * {Boolean} [queue=true] Indicates whether to place the animation + * in the effects queue to run when other animations have finished. + * If false, the animation will begin after `delay` milliseconds; + * likewise, if `delay` is non-zero `queue` defaults to `false` + * instead. + * * {String|Function|Object} [easing='linear'] Name of easing function + * used to calculate each step value, or a function, or a map of + * properties to either of the above. + */ + this['init'] = + function initAnimation(obj, duration, props, options){ + this.obj = obj; + this.duration = duration; + this.props = props; + + this.options = options = Y.extend({}, defOptions, options); + if ( !('queue' in options) ) + options.queue = !options.delay; + + this.fx = Y(this.props) + .reduce(function(fx, val, prop){ + var easing = lookupEasing(options.easing, prop); + return fx.push(new Fx(obj, duration, prop, val, easing)); + }, Y([]) ); + + // TODO: Handle Queuing -- should happen in target objects who provide the API + }; + + this['start'] = + function start(now){ + if (this.running) return; + this.started = now || new Date().getTime(); + this.running = true; + this.waiting = !!this.options.delay; + }; + + this['tick'] = + function tick(elapsed, now){ + if (this.waiting && (now < this.started+this.options.delay)) + return true; + + + this.waiting = false; + return true; + }; + +});