function Animator_class() { this.animations = []; this._interval = null; this._endTime = 0; this._onStep = null; this._onComplete = null; var self = this; this._stepHandler = function() { self._step(); }; }; /* * Sample Call: * * Animator.addAnimations([{ * obj: something, * properties: ['x', 'y'], * to: [100, 250], * duration: 1500, * handle: 'identifier' // handle does not have to be unique, can be used to cancel animations * }, { * obj: anotherthing, * property: 'x', * to: 150, * duration: 750, * delay: 750, // delay defaults to 0 (zero) * transition: 'easeInSine' // transition defaults to a linear method * }, { * obj: someelement, * setter: function(obj, val) { obj.prop = val; }, * from: 0, * to: 100, * duration: 1500, * transition: 'easeInCirc' * } * ]); */ Animator_class.prototype.addAnimations = function(animations, onStep, onComplete) { //if(this._interval) this.cancel(); //this.animations = animations; this._onStep = onStep; this._onComplete = onComplete; this._start(animations); }; Animator_class.prototype.cancelByHandle = function(handle) { var count = 0; for(var i = 0, len = this.animations.length; i < len; i++) { if(this.animations[i - count].handle != handle) continue; this.animations.splice(i - count, 1); count++; } if(this.animations.length < 1) this.cancelAll(); return count; }; Animator_class.prototype.cancelAll = function() { clearInterval(this._interval); this._interval = null; this.animations.length = 0; }; Animator_class.prototype.isRunning = function() { return !!this._interval; }; Animator_class.prototype._start = function(animations) { var startTime = new Date().getTime(); // Initialize the animations var animation = null; for(var i = 0, len = animations.length; i < len; i++) { animation = animations[i]; // Get timing information animation.delay = animation.delay ? animation.delay : 0; animation.startTime = startTime + animation.delay; animation.endTime = animation.startTime + animation.duration; if(animation.endTime > this._endTime) this._endTime = animation.endTime; // Set the transition function animation.transition = (animation.transition ? Animator_class.Transitions[animation.transition] : Animator_class.Transitions['easeIn']); // Convert property animations to properties animations if (animation.property && !animation.properties) { animation.properties = [animation.property]; animation.to = [animation.to]; } if(animation.properties) { animation.from = []; animation.change = []; for(var j = 0; j < animation.properties.length; j++) { animation.from[j] = animation.obj[animation.properties[j]]; animation.change[j] = animation.to[j] - animation.from[j]; } } else { animation.change = animation.to - animation.from; } // Add the animation to the running animations this.animations.push(animation); } if(!this.isRunning()) this._interval = setInterval(this._stepHandler, 1); }; Animator_class.prototype._step = function() { var time = new Date().getTime(); var animation = null, diff = 0, value = 0, finished = 0, property = '', from = 0, to = 0; for(var i = 0, len = this.animations.length; i < len; i++) { animation = this.animations[i - finished]; diff = time - animation.startTime; if(diff <= 0) continue; // See if we are done fading this element animation.complete = (diff >= animation.duration); if (animation.complete) { diff = animation.duration; } if(animation.properties) { for(var j = 0; j < animation.properties.length; j++) { property = animation.properties[j]; from = animation.from[j]; to = animation.to[j]; if(property == 'fillStyle') { for(var k = 0; k < 3; k++) { // "R", "G", and "B" go 0-255 and must be rounded to nearest integer animation.obj[property][k] = Math.round(animation.transition(diff, from[k], to[k] - from[k], animation.duration)); } // "A" goes 0-1 and cannot be rounded animation.obj[property][3] = Math.round(100000 * animation.transition(diff, from[3], to[3] - from[3], animation.duration)) / 100000; } else { animation.obj[property] = animation.transition(diff, from, to - from, animation.duration); } } } else { animation.setter(animation.obj, animation.transition(diff, animation.from, animation.change, animation.duration)); } // Remove the animation if completed if(animation.complete) { this.animations.splice(i - finished, 1); finished++; } } if(typeof this._onStep == 'function') this._onStep(); // Execute callback method and kill the interval if (time >= this._endTime) { this.cancelAll(); /* WSDOM.Console.log("fade complete: " + frames + " frames in " + (endTime - startTime) + "ms (" + Math.ceil(frames * 1000 / (endTime - startTime)) + "fps)"); */ if (typeof this._onComplete == 'function') this._onComplete(); } }; Animator_class.Transitions = { easeOutCubic: function(t,b,c,d){ return -c*((t=t/d-1)*t*t*t - 1) + b; }, easeInCirc: function(t,b,c,d) { return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; }, easeInSine: function (t,b,c,d) { return -c * Math.cos(t/d * (Math.PI/2)) + c + b; } }; var Animator = new Animator_class();