// Timeline.js v0.1 / 2011-05-01 // A compact JavaScript animation library with a GUI timeline for fast editing. // by Marcin Ignac (http://marcinignac.com) // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. var Timeline = function() { this.name = "Global"; this.anims = []; this.time = 0; this.totalTime = 0; this.loopCount = 0; this.loopMode = 0; this.playing = true; this.verbose = false; var self = this; //setInterval(function() { // self.update(); //}, 1000/30); }; Timeline.currentInstance = null; Timeline.getGlobalInstance = function() { if (!Timeline.globalInstance) { Timeline.globalInstance = new Timeline(); } return Timeline.globalInstance; }; Timeline.prevTime = Date.now(); Timeline.update = function() { var now = Date.now(); delta = (now - Timeline.prevTime)/1000; Timeline.prevTime = now; Timeline.getGlobalInstance().update() } //Possible values of n: //-1 infinite loop //0 play forever without looping, continue increasing time even after last animation //1 play once and stop at the time the last animation finishes //>1 loop n-times Timeline.prototype.loop = function(n) { this.loopMode = n; }; Timeline.prototype.stop = function() { this.playing = false; this.time = 0; this.prevTime = this.time - 1/30; //FIXME 1/30 }; Timeline.prototype.pause = function() { this.playing = false; }; Timeline.prototype.play = function() { this.playing = true; }; Timeline.prototype.preUpdate = function() { //placeholder for hooks like GUI rendering }; Timeline.prototype.update = function(deltaTime) { deltaTime = deltaTime || 1/30; this.preUpdate(); if (this.playing) { this.totalTime += deltaTime; this.prevTime = this.time; this.time += deltaTime; } if (this.loopMode !== 0) { var animationEnd = this.findAnimationEnd(); if (this.time > animationEnd) { this.loopCount++; this.time = 0; for(var i=0; i= this.loopMode) { this.playing = false; } } } this.applyValues(); }; Timeline.prototype.findAnimationEnd = function() { var endTime = 0; for(var i=0; i endTime) { endTime = this.anims[i].endTime; } } return endTime; }; Timeline.prototype.getValue = function(obj, name) { if (name == 'rotation') { if (typeof(obj._timelineRotation) == 'undefined') { obj._timelineRotation = obj.rotation || 0; } return obj._timelineRotation; } else if (name == 'scale') { if (typeof(obj._timelineScale) == 'undefined') { obj._timelineScale = 1; } return obj._timelineScale; } else { return obj[name]; } } Timeline.prototype.setValue = function(obj, name, value) { if (name == 'rotation') { obj.rotate(value - obj._timelineRotation); obj._timelineRotation = value; } else if (name == 'scale') { obj.scale(value/obj._timelineScale) obj._timelineScale = value; } else { obj[name] = value; } } Timeline.prototype.applyValues = function() { for(var i=0; i= propertyAnim.startTime && !propertyAnim.hasStarted) { propertyAnim.startValue = this.getValue(propertyAnim.target, propertyAnim.propertyName) propertyAnim.hasStarted = true; propertyAnim.onStart(); } var t = (this.time - propertyAnim.startTime)/(propertyAnim.endTime - propertyAnim.startTime); t = Math.max(0, Math.min(t, 1)); t = propertyAnim.easing(t); var value = propertyAnim.startValue + (propertyAnim.endValue - propertyAnim.startValue) * t; this.setValue(propertyAnim.target, propertyAnim.propertyName, value); if (propertyAnim.parent.onUpdateCallback) { propertyAnim.parent.onUpdateCallback(propertyAnim); } if (this.time >= propertyAnim.endTime && !propertyAnim.hasEnded) { propertyAnim.hasEnded = true; propertyAnim.onEnd(); } if (t == 1) { if (this.loopMode == 0) { this.anims.splice(i, 1); i--; } } } }; //-------------------------------------------------------------------- Timeline.Anim = function(name, target, timeline) { this.startTime = 0; this.endTime = 0; this.time = 0; this.propertyAnims = []; this.hasStarted = false; this.hasEnded = false; this.onStartCallbackCalled = false; this.onEndCallbackCalled = false; this.onUpdateCallbackCalled = false; this.name = name; this.target = target; this.timeline = timeline; this.animGroups = []; } //delay, properties, duration, easing Timeline.Anim.prototype.to = function() { var args = []; for(var i=0; i