From 77aea95e7a41c5326b768d9db54d3d4e886f3852 Mon Sep 17 00:00:00 2001 From: Eric Rowell Date: Mon, 2 Apr 2012 21:03:59 -0700 Subject: [PATCH] round 1 of animation + transition rewrite, while combing Xaric's Tween port --- Thorfile | 2 +- dist/kinetic-core.js | 654 +++++++++++++++++++++++++++--------- dist/kinetic-core.min.js | 5 +- src/GlobalObject.js | 287 ++++++++-------- src/Layer.js | 2 - src/Node.js | 15 +- src/Stage.js | 12 +- src/Transitions.js | 335 ++++++++++++++++++ tests/js/Test.js | 2 +- tests/js/functionalTests.js | 24 +- 10 files changed, 1010 insertions(+), 328 deletions(-) create mode 100644 src/Transitions.js diff --git a/Thorfile b/Thorfile index 61dfe5b0..9405a97e 100644 --- a/Thorfile +++ b/Thorfile @@ -6,7 +6,7 @@ class Build < Thor "license.js", "src/GlobalObject.js", "src/Node.js", "src/Container.js", "src/Stage.js", "src/Layer.js", "src/Group.js", "src/Shape.js", "src/shapes/Rect.js", "src/shapes/Circle.js", "src/shapes/Image.js", "src/shapes/Polygon.js", "src/shapes/RegularPolygon.js", "src/shapes/Star.js", "src/shapes/Text.js", - "src/Transform.js" + "src/Transform.js", "src/Transitions.js" ] desc "dev", "Concatenate all the js files into /dist/kinetic-VERSION.js." diff --git a/dist/kinetic-core.js b/dist/kinetic-core.js index 683db337..57c683ff 100644 --- a/dist/kinetic-core.js +++ b/dist/kinetic-core.js @@ -3,7 +3,7 @@ * http://www.kineticjs.com/ * Copyright 2012, Eric Rowell * Licensed under the MIT or GPL Version 2 licenses. - * Date: Apr 01 2012 + * Date: Apr 02 2012 * * Copyright (C) 2011 - 2012 by Eric Rowell * @@ -41,6 +41,8 @@ var Kinetic = {}; Kinetic.GlobalObject = { stages: [], idCounter: 0, + animations: [], + animIdCounter: 0, frame: { time: 0, timeDiff: 0, @@ -61,152 +63,141 @@ Kinetic.GlobalObject = { } } }, - _isaCanvasAnimating: function() { - for(var n = 0; n < this.stages.length; n++) { - var stage = this.stages[n]; - if(stage.isAnimating) { - return true; - } + /* + _endTransition: function() { + var config = this.config; + for(var key in config) { + if(config.hasOwnProperty(key)) { + if(config[key].x !== undefined || config[key].y !== undefined) { + var propArray = ['x', 'y']; + for(var n = 0; n < propArray.length; n++) { + var prop = propArray[n]; + if(config[key][prop] !== undefined) { + this.node[key][prop] = config[key][prop]; + } + } + } + else { + this.node[key] = config[key]; + } + } + } + }, + _transitionPow: function(transition, key, prop, powFunc) { + var pow = powFunc(); - for(var i = 0; i < stage.children.length; i++) { - var layer = stage.children[i]; - if(layer.transitions.length > 0) { - return true; - } - } - } - - this.frame.lastTime = 0; - return false; - }, - _endTransition: function() { - var config = this.config; - for(var key in config) { - if(config.hasOwnProperty(key)) { - if(config[key].x !== undefined || config[key].y !== undefined) { - var propArray = ['x', 'y']; - for(var n = 0; n < propArray.length; n++) { - var prop = propArray[n]; - if(config[key][prop] !== undefined) { - this.node[key][prop] = config[key][prop]; - } - } - } - else { - this.node[key] = config[key]; - } - } - } - }, - _transitionPow: function(transition, key, prop, powFunc) { - var pow = powFunc(); - - var config = transition.config; - var timeDiff = this.frame.timeDiff; - if(prop !== undefined) { - var start = transition.starts[key][prop]; - var change = config[key][prop] - start; - var b = change / (Math.pow(config.duration * 1000, pow)); - transition.node[key][prop] = b * Math.pow(transition.time, pow) + start; - } - else { - var start = transition.starts[key]; - var change = config[key] - start; - var b = change / (Math.pow(config.duration * 1000, pow)); - transition.node[key] = b * Math.pow(transition.time, pow) + start; - } - }, - _chooseTransition: function(transition, key, prop) { - var config = transition.config; - var that = this; - switch(config.easing) { - case 'ease-in': - this._transitionPow(transition, key, prop, function() { - return 2.5; - }); - break; - case 'ease-out': - this._transitionPow(transition, key, prop, function() { - return 0.4; - }); - break; - case 'ease-in-out': - this._transitionPow(transition, key, prop, function() { - var change = -2.1; - var b = change / (config.duration * 1000); - return 2.5 + b * transition.time; - }); - break; - // linear is default - default: - this._transitionPow(transition, key, prop, function() { - return 1; - }); - break; - } - }, - _runTransition: function(transition) { - var config = transition.config; - for(var key in config) { - if(config.hasOwnProperty(key) && key !== 'duration' && key !== 'easing' && key !== 'callback') { - if(config[key].x !== undefined || config[key].y !== undefined) { - var propArray = ['x', 'y']; - for(var n = 0; n < propArray.length; n++) { - var prop = propArray[n]; - if(config[key][prop] !== undefined) { - this._chooseTransition(transition, key, prop); - } - } - } - else { - this._chooseTransition(transition, key); - } - } - } - }, - _clearTransition: function(node) { - var layer = node.getLayer(); - for(var n = 0; n < layer.transitions.length; n++) { - if(layer.transitions[n].node.id === node.id) { - layer.transitions.splice(n, 1); - } - } - }, + var config = transition.config; + var timeDiff = this.frame.timeDiff; + if(prop !== undefined) { + var start = transition.starts[key][prop]; + var change = config[key][prop] - start; + var b = change / (Math.pow(config.duration * 1000, pow)); + transition.node[key][prop] = b * Math.pow(transition.time, pow) + start; + } + else { + var start = transition.starts[key]; + var change = config[key] - start; + var b = change / (Math.pow(config.duration * 1000, pow)); + transition.node[key] = b * Math.pow(transition.time, pow) + start; + } + }, + _chooseTransition: function(transition, key, prop) { + var config = transition.config; + var that = this; + switch(config.easing) { + case 'ease-in': + this._transitionPow(transition, key, prop, function() { + return 2.5; + }); + break; + case 'ease-out': + this._transitionPow(transition, key, prop, function() { + return 0.4; + }); + break; + case 'ease-in-out': + this._transitionPow(transition, key, prop, function() { + var change = -2.1; + var b = change / (config.duration * 1000); + return 2.5 + b * transition.time; + }); + break; + // linear is default + default: + this._transitionPow(transition, key, prop, function() { + return 1; + }); + break; + } + }, + _runTransition: function(transition) { + var config = transition.config; + for(var key in config) { + if(config.hasOwnProperty(key) && key !== 'duration' && key !== 'easing' && key !== 'callback') { + if(config[key].x !== undefined || config[key].y !== undefined) { + var propArray = ['x', 'y']; + for(var n = 0; n < propArray.length; n++) { + var prop = propArray[n]; + if(config[key][prop] !== undefined) { + this._chooseTransition(transition, key, prop); + } + } + } + else { + this._chooseTransition(transition, key); + } + } + } + }, + _clearTransition: function(node) { + var layer = node.getLayer(); + for(var n = 0; n < layer.transitions.length; n++) { + if(layer.transitions[n].node.id === node.id) { + layer.transitions.splice(n, 1); + } + } + }, + */ _runFrames: function() { - for(var n = 0; n < this.stages.length; n++) { - var stage = this.stages[n]; - // run animation if available - if(stage.isAnimating && stage.onFrameFunc !== undefined) { - stage.onFrameFunc(this.frame); - } - - // loop through layers - var layers = stage.getChildren(); - for(var k = 0; k < layers.length; k++) { - var layer = layers[k]; - var didTransition = false; - // loop through transitions - for(var i = 0; i < layer.transitions.length; i++) { - didTransition = true; - var transition = layer.transitions[i]; - transition.time += this.frame.timeDiff; - if(transition.time >= transition.config.duration * 1000) { - this._endTransition.apply(transition); - this._clearTransition(transition.node); - if(transition.config.callback !== undefined) { - transition.config.callback(); - } - } - else { - this._runTransition(transition); - } - } - - if(didTransition) { - layer.draw(); - } - } + for(var n = 0; n < this.animations.length; n++) { + this.animations[n].func(this.frame); } + /* + for(var n = 0; n < this.stages.length; n++) { + var stage = this.stages[n]; + // run animation if available + if(stage.isAnimating && stage.onFrameFunc !== undefined) { + stage.onFrameFunc(this.frame); + } + + // loop through layers + var layers = stage.getChildren(); + for(var k = 0; k < layers.length; k++) { + var layer = layers[k]; + var didTransition = false; + // loop through transitions + for(var i = 0; i < layer.transitions.length; i++) { + didTransition = true; + var transition = layer.transitions[i]; + transition.time += this.frame.timeDiff; + if(transition.time >= transition.config.duration * 1000) { + this._endTransition.apply(transition); + this._clearTransition(transition.node); + if(transition.config.callback !== undefined) { + transition.config.callback(); + } + } + else { + this._runTransition(transition); + } + } + + if(didTransition) { + layer.draw(); + } + } + } + */ }, _updateFrameObject: function() { var date = new Date(); @@ -221,7 +212,7 @@ Kinetic.GlobalObject = { } }, _animationLoop: function() { - if(this._isaCanvasAnimating()) { + if(this.animations.length > 0) { this._updateFrameObject(); this._runFrames(); var that = this; @@ -229,12 +220,18 @@ Kinetic.GlobalObject = { that._animationLoop(); }); } + else { + this.frame.lastTime = 0; + } }, _handleAnimation: function() { var that = this; - if(this._isaCanvasAnimating()) { + if(this.animations.length > 0) { that._animationLoop(); } + else { + this.frame.lastTime = 0; + } } }; @@ -729,15 +726,19 @@ Kinetic.Node.prototype = { transitionTo: function(config) { var layer = this.getLayer(); var that = this; - var duration = config.duration * 1000; - var starts = {}; + + + //var duration = config.duration * 1000; + //var starts = {}; + //var go = Kinetic.GlobalObject; /* * clear transition if one is currenlty running. * This make it easy to start new transitions without * having to explicitly cancel old ones */ - Kinetic.GlobalObject._clearTransition(this); + /* + go._clearTransition(this); for(var key in config) { if(config.hasOwnProperty(key) && key !== 'duration' && key !== 'easing' && key !== 'callback') { @@ -757,15 +758,16 @@ Kinetic.Node.prototype = { } } - layer.transitions.push({ + go.transitions.push({ id: layer.transitionIdCounter++, time: 0, config: config, node: this, starts: starts }); + */ - Kinetic.GlobalObject._handleAnimation(); + go._handleAnimation(); }, /** * set drag constraint @@ -1080,10 +1082,6 @@ Kinetic.Stage = function(config) { // set stage id this.id = Kinetic.GlobalObject.idCounter++; - // animation support - this.isAnimating = false; - this.onFrameFunc = undefined; - this._buildDOM(); this._listen(); this._prepareDrag(); @@ -1104,20 +1102,22 @@ Kinetic.Stage.prototype = { * @param {function} func */ onFrame: function(func) { - this.onFrameFunc = func; + var go = Kinetic.GlobalObject; + go.animations.push({ + id: go.animIdCounter++, + func: func + }); }, /** * start animation */ start: function() { - this.isAnimating = true; Kinetic.GlobalObject._handleAnimation(); }, /** * stop animation */ stop: function() { - this.isAnimating = false; Kinetic.GlobalObject._handleAnimation(); }, /** @@ -1796,8 +1796,6 @@ Kinetic.Layer = function(config) { this.canvas = document.createElement('canvas'); this.context = this.canvas.getContext('2d'); this.canvas.style.position = 'absolute'; - this.transitions = []; - this.transitionIdCounter = 0; // call super constructors Kinetic.Container.apply(this, []); @@ -2923,3 +2921,339 @@ Kinetic.Transform.prototype = { } }; +Kinetic.Transition = function(obj, propFunc, func, begin, finish, duration) { + this._listeners = []; + this.addListener(this); + this.obj = obj; + this.propFunc = propFunc; + this.begin = begin; + this._pos = begin; + this.setDuration(duration); + + this.isPlaying = false; + + this._change = 0; + + this.prevTime = 0; + this.prevPos = 0; + this.looping = false; + + this._time = 0; + this._position = 0; + this._startTime = 0; + this._finish = 0; + + this.name = ''; + + //set the tweening function + if(func !== null && func !== '') { + this.func = func; + } + else { + this.func = function(t, b, c, d) { + return c * t / d + b; + }; + } + + this.setFinish(finish); +}; + +Kinetic.Transition.prototype = { + setTime: function(t) { + this.prevTime = this._time; + if(t > this.getDuration()) { + if(this.looping) { + this.rewind(t - this._duration); + this.update(); + this.broadcastMessage('onTweenLooped', { + target: this, + type: 'onTweenLooped' + }); + } + else { + this._time = this._duration; + this.update(); + this.stop(); + this.broadcastMessage('onTweenFinished', { + target: this, + type: 'onTweenFinished' + }); + } + } + else if(t < 0) { + this.rewind(); + this.update(); + } + else { + this._time = t; + this.update(); + } + }, + getTime: function() { + return this._time; + }, + setDuration: function(d) { + this._duration = (d == null || d <= 0) ? 100000 : d; + }, + getDuration: function() { + return this._duration; + }, + setPosition: function(p) { + this.prevPos = this._pos; + //var a = this.suffixe != '' ? this.suffixe : ''; + this.propFunc(p); + //+ a; + //this.obj(Math.round(p)); + this._pos = p; + this.broadcastMessage('onTweenChanged', { + target: this, + type: 'onTweenChanged' + }); + }, + getPosition: function(t) { + if(t == undefined) + t = this._time; + return this.func(t, this.begin, this._change, this._duration); + }, + setFinish: function(f) { + this._change = f - this.begin; + }, + getFinish: function() { + return this.begin + this._change; + }, + start: function() { + this.rewind(); + this.startEnterFrame(); + this.broadcastMessage('onTweenStarted', { + target: this, + type: 'onTweenStarted' + }); + }, + rewind: function(t) { + this.stop(); + this._time = (t == undefined) ? 0 : t; + this.fixTime(); + this.update(); + }, + fforward: function() { + this._time = this._duration; + this.fixTime(); + this.update(); + }, + update: function() { + this.setPosition(this.getPosition(this._time)); + }, + startEnterFrame: function() { + this.stopEnterFrame(); + this.isPlaying = true; + this.onEnterFrame(); + }, + onEnterFrame: function() { + if(this.isPlaying) + this.nextFrame(); + }, + nextFrame: function() { + this.setTime((this.getTimer() - this._startTime) / 1000); + }, + stop: function() { + this.stopEnterFrame(); + this.broadcastMessage('onTweenStopped', { + target: this, + type: 'onTweenStopped' + }); + }, + stopEnterFrame: function() { + this.isPlaying = false; + }, + continueTo: function(finish, duration) { + this.begin = this._pos; + this.setFinish(finish); + if(this._duration != undefined) + this.setDuration(duration); + this.start(); + }, + resume: function() { + this.fixTime(); + this.startEnterFrame(); + this.broadcastMessage('onTweenResumed', { + target: this, + type: 'onTweenResumed' + }); + }, + yoyo: function() { + this.continueTo(this.begin, this._time); + }, + addListener: function(o) { + this.removeListener(o); + return this._listeners.push(o); + }, + removeListener: function(o) { + var a = this._listeners; + var i = a.length; + while(i--) { + if(a[i] == o) { + a.splice(i, 1); + return true; + } + } + return false; + }, + broadcastMessage: function() { + var arr = new Array(); + for(var i = 0; i < arguments.length; i++) { + arr.push(arguments[i]) + } + var e = arr.shift(); + var a = this._listeners; + var l = a.length; + for(var i = 0; i < l; i++) { + if(a[i][e]) + a[i][e].apply(a[i], arr); + } + }, + fixTime: function() { + this._startTime = this.getTimer() - this._time * 1000; + }, + getTimer: function() { + return new Date().getTime() - this._time; + } +}; + +Kinetic.Transitions = { + backEaseIn: function(t, b, c, d, a, p) { + var s = 1.70158; + return c * (t /= d) * t * ((s + 1) * t - s) + b; + }, + backEaseOut: function(t, b, c, d, a, p) { + var s = 1.70158; + return c * (( t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; + }, + backEaseInOut: function(t, b, c, d, a, p) { + var s = 1.70158; + if((t /= d / 2) < 1) { + return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b; + } + return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b; + }, + elasticEaseIn: function(t, b, c, d, a, p) { + // added s = 0 + var s = 0; + if(t === 0) { + return b; + } + if((t /= d) == 1) { + return b + c; + } + if(!p) { + p = d * 0.3; + } + if(!a || a < Math.abs(c)) { + a = c; + s = p / 4; + } + else { + s = p / (2 * Math.PI) * Math.asin(c / a); + } + return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; + }, + elasticEaseOut: function(t, b, c, d, a, p) { + // added s = 0 + var s = 0; + if(t === 0) { + return b; + } + if((t /= d) == 1) { + return b + c; + } + if(!p) { + p = d * 0.3; + } + if(!a || a < Math.abs(c)) { + a = c; + s = p / 4; + } + else { + s = p / (2 * Math.PI) * Math.asin(c / a); + } + return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b); + }, + elasticEaseInOut: function(t, b, c, d, a, p) { + var s = 0; + if(t === 0) { + return b; + } + if((t /= d / 2) == 2) { + return b + c; + } + if(!p) { + p = d * (0.3 * 1.5); + } + if(!a || a < Math.abs(c)) { + a = c; + s = p / 4; + } + else { + s = p / (2 * Math.PI) * Math.asin(c / a); + } + if(t < 1) { + return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; + } + return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b; + }, + bounceEaseOut: function(t, b, c, d) { + if((t /= d) < (1 / 2.75)) { + return c * (7.5625 * t * t) + b; + } + else if(t < (2 / 2.75)) { + return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b; + } + else if(t < (2.5 / 2.75)) { + return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b; + } + else { + return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b; + } + }, + bounceEaseIn: function(t, b, c, d) { + return c - Kinetic.Transitions.bounceEaseOut(d - t, 0, c, d) + b; + }, + bounceEaseInOut: function(t, b, c, d) { + if(t < d / 2) { + return Kinetic.Transitions.bounceEaseIn(t * 2, 0, c, d) * 0.5 + b; + } + else { + return Kinetic.Transitions.bounceEaseOut(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; + } + }, + // duplicate + /* + strongEaseInOut: function(t, b, c, d) { + return c * (t /= d) * t * t * t * t + b; + }, + */ + regularEaseIn: function(t, b, c, d) { + return c * (t /= d) * t + b; + }, + regularEaseOut: function(t, b, c, d) { + return -c * (t /= d) * (t - 2) + b; + }, + regularEaseInOut: function(t, b, c, d) { + if((t /= d / 2) < 1) { + return c / 2 * t * t + b; + } + return -c / 2 * ((--t) * (t - 2) - 1) + b; + }, + strongEaseIn: function(t, b, c, d) { + return c * (t /= d) * t * t * t * t + b; + }, + strongEaseOut: function(t, b, c, d) { + return c * (( t = t / d - 1) * t * t * t * t + 1) + b; + }, + strongEaseInOut: function(t, b, c, d) { + if((t /= d / 2) < 1) { + return c / 2 * t * t * t * t * t + b; + } + return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; + } +}; + diff --git a/dist/kinetic-core.min.js b/dist/kinetic-core.min.js index 4fc1a8a2..76426ff4 100644 --- a/dist/kinetic-core.min.js +++ b/dist/kinetic-core.min.js @@ -3,7 +3,7 @@ * http://www.kineticjs.com/ * Copyright 2012, Eric Rowell * Licensed under the MIT or GPL Version 2 licenses. - * Date: Apr 01 2012 + * Date: Apr 02 2012 * * Copyright (C) 2011 - 2012 by Eric Rowell * @@ -25,4 +25,5 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -var Kinetic={};Kinetic.GlobalObject={stages:[],idCounter:0,frame:{time:0,timeDiff:0,lastTime:0},drag:{moving:!1,node:undefined,offset:{x:0,y:0}},extend:function(a,b){for(var c in b.prototype)b.prototype.hasOwnProperty(c)&&a.prototype[c]===undefined&&(a.prototype[c]=b.prototype[c])},_isaCanvasAnimating:function(){for(var a=0;a0)return!0}}return this.frame.lastTime=0,!1},_endTransition:function(){var a=this.config;for(var b in a)if(a.hasOwnProperty(b))if(a[b].x!==undefined||a[b].y!==undefined){var c=["x","y"];for(var d=0;d=h.config.duration*1e3?(this._endTransition.apply(h),this._clearTransition(h.node),h.config.callback!==undefined&&h.config.callback()):this._runTransition(h)}f&&e.draw()}}},_updateFrameObject:function(){var a=new Date,b=a.getTime();this.frame.lastTime===0?this.frame.lastTime=b:(this.frame.timeDiff=b-this.frame.lastTime,this.frame.lastTime=b,this.frame.time+=this.frame.timeDiff)},_animationLoop:function(){if(this._isaCanvasAnimating()){this._updateFrameObject(),this._runFrames();var a=this;requestAnimFrame(function(){a._animationLoop()})}},_handleAnimation:function(){var a=this;this._isaCanvasAnimating()&&a._animationLoop()}},window.requestAnimFrame=function(a){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1e3/60)}}(),Kinetic.Node=function(a){this.visible=!0,this.isListening=!0,this.name=undefined,this.alpha=1,this.x=0,this.y=0,this.scale={x:1,y:1},this.rotation=0,this.centerOffset={x:0,y:0},this.eventListeners={},this.dragConstraint="none",this.dragBounds={},this._draggable=!1;if(a)for(var b in a)switch(b){case"draggable":this.draggable(a[b]);break;case"listen":this.listen(a[b]);break;case"rotationDeg":this.rotation=a[b]*Math.PI/180;break;default:this[b]=a[b]}this.centerOffset.x===undefined&&(this.centerOffset.x=0),this.centerOffset.y===undefined&&(this.centerOffset.y=0)},Kinetic.Node.prototype={on:function(a,b){var c=a.split(" ");for(var d=0;d1?g[1]:"";this.eventListeners[h]||(this.eventListeners[h]=[]),this.eventListeners[h].push({name:i,handler:b})}},off:function(a){var b=a.split(" ");for(var c=0;c1){var h=f[1];for(var i=0;i0&&f[0].getLevel()<=a&&e(f)}var a=this.getLevel(),b=this.getStage(),c=this,d=0;return c.className!=="Stage"&&e(c.getStage().getChildren()),d},getLevel:function(){var a=0,b=this.parent;while(b)a++,b=b.parent;return a},setScale:function(a,b){b?(this.scale.x=a,this.scale.y=b):(this.scale.x=a,this.scale.y=a)},getScale:function(){return this.scale},setPosition:function(a,b){this.x=a,this.y=b},getPosition:function(){return{x:this.x,y:this.y}},getAbsolutePosition:function(){return this.getAbsoluteTransform().getTranslation()},move:function(a,b){this.x+=a,this.y+=b},setRotation:function(a){this.rotation=a},setRotationDeg:function(a){this.rotation=a*Math.PI/180},getRotation:function(){return this.rotation},getRotationDeg:function(){return this.rotation*180/Math.PI},rotate:function(a){this.rotation+=a},rotateDeg:function(a){this.rotation+=a*Math.PI/180},listen:function(a){this.isListening=a},moveToTop:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.push(this),this.parent._setChildrenIndices()},moveUp:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.splice(a+1,0,this),this.parent._setChildrenIndices()},moveDown:function(){var a=this.index;a>0&&(this.parent.children.splice(a,1),this.parent.children.splice(a-1,0,this),this.parent._setChildrenIndices())},moveToBottom:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.unshift(this),this.parent._setChildrenIndices()},setZIndex:function(a){var b=this.index;this.parent.children.splice(b,1),this.parent.children.splice(a,0,this),this.parent._setChildrenIndices()},setAlpha:function(a){this.alpha=a},getAlpha:function(){return this.alpha},getAbsoluteAlpha:function(){var a=1,b=this;while(b.className!=="Stage")a*=b.alpha,b=b.parent;return a},draggable:function(a){this.draggable!==a&&(a?this._initDrag():this._dragCleanup(),this._draggable=a)},isDragging:function(){var a=Kinetic.GlobalObject;return a.drag.node!==undefined&&a.drag.node.id===this.id&&a.drag.moving},moveTo:function(a){var b=this.parent;b.children.splice(this.index,1),b._setChildrenIndices(),a.children.push(this),this.index=a.children.length-1,this.parent=a,a._setChildrenIndices(),this.name&&(b.childrenNames[this.name]=undefined,a.childrenNames[this.name]=this)},getParent:function(){return this.parent},getLayer:function(){return this.className==="Layer"?this:this.getParent().getLayer()},getStage:function(){return this.className==="Stage"?this:this.getParent().getStage()},getName:function(){return this.name},setCenterOffset:function(a,b){this.centerOffset.x=a,this.centerOffset.y=b},getCenterOffset:function(){return this.centerOffset},transitionTo:function(a){var b=this.getLayer(),c=this,d=a.duration*1e3,e={};Kinetic.GlobalObject._clearTransition(this);for(var f in a)if(a.hasOwnProperty(f)&&f!=="duration"&&f!=="easing"&&f!=="callback")if(a[f].x!==undefined||a[f].y!==undefined){e[f]={};var g=["x","y"];for(var h=0;h0)this.remove(this.children[0])},_remove:function(a){this.children[a.index].id==a.id&&(a.name!==undefined&&(this.childrenNames[a.name]=undefined),this.children.splice(a.index,1),this._setChildrenIndices(),a=undefined)},_drawChildren:function(){var a=this.children;for(var b=0;b=0;d--){var e=c[d];if(e.isListening)if(e.className==="Shape"){var f=this._detectEvent(e,b);if(f)return!0}else{var f=this._traverseChildren(e,b);if(f)return!0}}return!1},_handleStageEvent:function(a){var b=Kinetic.GlobalObject;a||(a=window.event),this._setMousePosition(a),this._setTouchPosition(a),this.pathLayer.clear(),this.targetFound=!1;var c=!1;for(var d=this.children.length-1;d>=0;d--){var e=this.children[d];e.visible&&d>=0&&e.isListening&&this._traverseChildren(e,a)&&(d=-1,c=!0)}!c&&this.mouseoutShape&&(this.mouseoutShape._handleEvents("onmouseout",a),this.mouseoutShape=undefined)},_listen:function(){var a=this;this.content.addEventListener("mousedown",function(b){a.mouseDown=!0,a._handleStageEvent(b)},!1),this.content.addEventListener("mousemove",function(b){a.mouseUp=!1,a.mouseDown=!1,a._handleStageEvent(b)},!1),this.content.addEventListener("mouseup",function(b){a.mouseUp=!0,a.mouseDown=!1,a._handleStageEvent(b),a.clickStart=!1},!1),this.content.addEventListener("mouseover",function(b){a._handleStageEvent(b)},!1),this.content.addEventListener("mouseout",function(b){var c=a.targetShape;c&&c._handleEvents("onmouseout",b),a.mousePos=undefined},!1),this.content.addEventListener("touchstart",function(b){b.preventDefault(),a.touchStart=!0,a._handleStageEvent(b)},!1),this.content.addEventListener("touchmove",function(b){b.preventDefault(),a._handleStageEvent(b)},!1),this.content.addEventListener("touchend",function(b){b.preventDefault(),a.touchEnd=!0,a._handleStageEvent(b)},!1)},_setMousePosition:function(a){var b=a.offsetX||a.clientX-this._getContentPosition().left+window.pageXOffset,c=a.offsetY||a.clientY-this._getContentPosition().top+window.pageYOffset;this.mousePos={x:b,y:c}},_setTouchPosition:function(a){if(a.touches!==undefined&&a.touches.length===1){var b=a.touches[0],c=b.clientX-this._getContentPosition().left+window.pageXOffset,d=b.clientY-this._getContentPosition().top+window.pageYOffset;this.touchPos={x:c,y:d}}},_getContentPosition:function(){var a=this.content,b=0,c=0;while(a&&a.tagName!=="BODY")b+=a.offsetTop-a.scrollTop,c+=a.offsetLeft-a.scrollLeft,a=a.offsetParent;return{top:b,left:c}},_modifyPathContext:function(a){a.stroke=function(){},a.fill=function(){},a.fillRect=function(a,b,c,d){layer.context.rect(a,b,c,d)},a.strokeRect=function(a,b,c,d){layer.context.rect(a,b,c,d)},a.drawImage=function(){},a.fillText=function(){},a.strokeText=function(){}},_endDrag:function(a){var b=Kinetic.GlobalObject;b.drag.node&&b.drag.moving&&(b.drag.moving=!1,b.drag.node._handleEvents("ondragend",a)),b.drag.node=undefined},_prepareDrag:function(){var a=this;this.onContent("mousemove touchmove",function(b){var c=Kinetic.GlobalObject,d=c.drag.node;if(d){var e=a.getUserPosition(),f=d.dragConstraint,g=d.dragBounds,h={x:e.x-c.drag.offset.x,y:e.y-c.drag.offset.y};g.left!==undefined&&h.xg.right&&(h.x=g.right),g.top!==undefined&&h.yg.bottom&&(h.y=g.bottom);var i=d.rotation,j={x:d.scale.x,y:d.scale.y};d.rotation=0,d.scale={x:1,y:1};var k=d.getAbsoluteTransform();k.invert(),k.translate(h.x,h.y),h={x:d.x+k.getTranslation().x,y:d.y+k.getTranslation().y},f==="horizontal"?h.y=d.y:f==="vertical"&&(h.x=d.x),d.setPosition(h.x,h.y),d.rotate(i),d.scale={x:j.x,y:j.y},c.drag.node.getLayer().draw(),c.drag.moving||(c.drag.moving=!0,c.drag.node._handleEvents("ondragstart",b)),c.drag.node._handleEvents("ondragmove",b)}},!1),this.onContent("mouseup touchend mouseout",function(b){a._endDrag(b)})},_buildDOM:function(){this.content.style.width=this.width+"px",this.content.style.height=this.height+"px",this.content.style.position="relative",this.content.style.display="inline-block",this.content.className="kineticjs-content",this.container.appendChild(this.content),this.bufferLayer=new Kinetic.Layer({name:"bufferLayer"}),this.pathLayer=new Kinetic.Layer({name:"pathLayer"}),this.bufferLayer.parent=this,this.pathLayer.parent=this,this._modifyPathContext(this.pathLayer.context),this.bufferLayer.getCanvas().style.display="none",this.pathLayer.getCanvas().style.display="none",this.bufferLayer.canvas.width=this.width,this.bufferLayer.canvas.height=this.height,this.bufferLayer.canvas.className="kineticjs-buffer-layer",this.content.appendChild(this.bufferLayer.canvas),this.pathLayer.canvas.width=this.width,this.pathLayer.canvas.height=this.height,this.pathLayer.canvas.className="kineticjs-path-layer",this.content.appendChild(this.pathLayer.canvas)}},Kinetic.GlobalObject.extend(Kinetic.Stage,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Stage,Kinetic.Node),Kinetic.Layer=function(a){this.className="Layer",this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),this.canvas.style.position="absolute",this.transitions=[],this.transitionIdCounter=0,Kinetic.Container.apply(this,[]),Kinetic.Node.apply(this,[a])},Kinetic.Layer.prototype={draw:function(){this._draw()},clear:function(){var a=this.getContext(),b=this.getCanvas();a.clearRect(0,0,b.width,b.height)},getCanvas:function(){return this.canvas},getContext:function(){return this.context},add:function(a){this._add(a)},remove:function(a){this._remove(a)},_draw:function(){this.clear(),this.visible&&this._drawChildren()}},Kinetic.GlobalObject.extend(Kinetic.Layer,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Layer,Kinetic.Node),Kinetic.Group=function(a){this.className="Group",Kinetic.Container.apply(this,[]),Kinetic.Node.apply(this,[a])},Kinetic.Group.prototype={add:function(a){this._add(a)},remove:function(a){this._remove(a)},_draw:function(){this.visible&&this._drawChildren()}},Kinetic.GlobalObject.extend(Kinetic.Group,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Group,Kinetic.Node),Kinetic.Shape=function(a){this.className="Shape",this.data=[];if(a.stroke!==undefined||a.strokeWidth!==undefined)a.stroke===undefined?a.stroke="black":a.strokeWidth===undefined&&(a.strokeWidth=2);this.detectionType="path",this.drawFunc=a.drawFunc,Kinetic.Node.apply(this,[a])},Kinetic.Shape.prototype={getContext:function(){return this.tempLayer.getContext()},getCanvas:function(){return this.tempLayer.getCanvas()},fillStroke:function(){var a=this.getContext();this.fill!==undefined&&(a.fillStyle=this.fill,a.fill()),this.stroke!==undefined&&(a.lineWidth=this.strokeWidth===undefined?1:this.strokeWidth,a.strokeStyle=this.stroke,a.stroke())},applyLineJoin:function(){var a=this.getContext();this.lineJoin!==undefined&&(a.lineJoin=this.lineJoin)},setFill:function(a){this.fill=a},getFill:function(){return this.fill},setStroke:function(a){this.stroke=a},getStroke:function(){return this.stroke},setLineJoin:function(a){this.lineJoin=a},getLineJoin:function(){return this.lineJoin},setStrokeWidth:function(a){this.strokeWidth=a},getStrokeWidth:function(){return this.strokeWidth},setDrawFunc:function(a){this.drawFunc=a},save:function(){var a=this.getStage(),b=a.width,c=a.height,d=a.bufferLayer,e=d.getContext();d.clear(),this._draw(d);var f=e.getImageData(0,0,b,c);this.data=f.data},_draw:function(a){if(this.visible){var b=a.getStage(),c=a.getContext(),d=[],e=this.parent;d.unshift(this);while(e)d.unshift(e),e=e.parent;c.save();for(var f=0;f0){this._updateFrameObject(),this._runFrames();var a=this;requestAnimFrame(function(){a._animationLoop()})}else this.frame.lastTime=0},_handleAnimation:function(){var a=this;this.animations.length>0?a._animationLoop():this.frame.lastTime=0}},window.requestAnimFrame=function(a){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1e3/60)}}(),Kinetic.Node=function(a){this.visible=!0,this.isListening=!0,this.name=undefined,this.alpha=1,this.x=0,this.y=0,this.scale={x:1,y:1},this.rotation=0,this.centerOffset={x:0,y:0},this.eventListeners={},this.dragConstraint="none",this.dragBounds={},this._draggable=!1;if(a)for(var b in a)switch(b){case"draggable":this.draggable(a[b]);break;case"listen":this.listen(a[b]);break;case"rotationDeg":this.rotation=a[b]*Math.PI/180;break;default:this[b]=a[b]}this.centerOffset.x===undefined&&(this.centerOffset.x=0),this.centerOffset.y===undefined&&(this.centerOffset.y=0)},Kinetic.Node.prototype={on:function(a,b){var c=a.split(" ");for(var d=0;d1?g[1]:"";this.eventListeners[h]||(this.eventListeners[h]=[]),this.eventListeners[h].push({name:i,handler:b})}},off:function(a){var b=a.split(" ");for(var c=0;c1){var h=f[1];for(var i=0;i0&&f[0].getLevel()<=a&&e(f)}var a=this.getLevel(),b=this.getStage(),c=this,d=0;return c.className!=="Stage"&&e(c.getStage().getChildren()),d},getLevel:function(){var a=0,b=this.parent;while(b)a++,b=b.parent;return a},setScale:function(a,b){b?(this.scale.x=a,this.scale.y=b):(this.scale.x=a,this.scale.y=a)},getScale:function(){return this.scale},setPosition:function(a,b){this.x=a,this.y=b},getPosition:function(){return{x:this.x,y:this.y}},getAbsolutePosition:function(){return this.getAbsoluteTransform().getTranslation()},move:function(a,b){this.x+=a,this.y+=b},setRotation:function(a){this.rotation=a},setRotationDeg:function(a){this.rotation=a*Math.PI/180},getRotation:function(){return this.rotation},getRotationDeg:function(){return this.rotation*180/Math.PI},rotate:function(a){this.rotation+=a},rotateDeg:function(a){this.rotation+=a*Math.PI/180},listen:function(a){this.isListening=a},moveToTop:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.push(this),this.parent._setChildrenIndices()},moveUp:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.splice(a+1,0,this),this.parent._setChildrenIndices()},moveDown:function(){var a=this.index;a>0&&(this.parent.children.splice(a,1),this.parent.children.splice(a-1,0,this),this.parent._setChildrenIndices())},moveToBottom:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.unshift(this),this.parent._setChildrenIndices()},setZIndex:function(a){var b=this.index;this.parent.children.splice(b,1),this.parent.children.splice(a,0,this),this.parent._setChildrenIndices()},setAlpha:function(a){this.alpha=a},getAlpha:function(){return this.alpha},getAbsoluteAlpha:function(){var a=1,b=this;while(b.className!=="Stage")a*=b.alpha,b=b.parent;return a},draggable:function(a){this.draggable!==a&&(a?this._initDrag():this._dragCleanup(),this._draggable=a)},isDragging:function(){var a=Kinetic.GlobalObject;return a.drag.node!==undefined&&a.drag.node.id===this.id&&a.drag.moving},moveTo:function(a){var b=this.parent;b.children.splice(this.index,1),b._setChildrenIndices(),a.children.push(this),this.index=a.children.length-1,this.parent=a,a._setChildrenIndices(),this.name&&(b.childrenNames[this.name]=undefined,a.childrenNames[this.name]=this)},getParent:function(){return this.parent},getLayer:function(){return this.className==="Layer"?this:this.getParent().getLayer()},getStage:function(){return this.className==="Stage"?this:this.getParent().getStage()},getName:function(){return this.name},setCenterOffset:function(a,b){this.centerOffset.x=a,this.centerOffset.y=b},getCenterOffset:function(){return this.centerOffset},transitionTo:function(a){var b=this.getLayer(),c=this;go._handleAnimation()},setDragConstraint:function(a){this.dragConstraint=a},getDragConstraint:function(){return this.dragConstraint},setDragBounds:function(a){this.dragBounds=a},getDragBounds:function(){return this.dragBounds},getAbsoluteTransform:function(){var a=new Kinetic.Transform,b=[],c=this.parent;b.unshift(this);while(c)b.unshift(c),c=c.parent;for(var d=0;d0)this.remove(this.children[0])},_remove:function(a){this.children[a.index].id==a.id&&(a.name!==undefined&&(this.childrenNames[a.name]=undefined),this.children.splice(a.index,1),this._setChildrenIndices(),a=undefined)},_drawChildren:function(){var a=this.children;for(var b=0;b=0;d--){var e=c[d];if(e.isListening)if(e.className==="Shape"){var f=this._detectEvent(e,b);if(f)return!0}else{var f=this._traverseChildren(e,b);if(f)return!0}}return!1},_handleStageEvent:function(a){var b=Kinetic.GlobalObject;a||(a=window.event),this._setMousePosition(a),this._setTouchPosition(a),this.pathLayer.clear(),this.targetFound=!1;var c=!1;for(var d=this.children.length-1;d>=0;d--){var e=this.children[d];e.visible&&d>=0&&e.isListening&&this._traverseChildren(e,a)&&(d=-1,c=!0)}!c&&this.mouseoutShape&&(this.mouseoutShape._handleEvents("onmouseout",a),this.mouseoutShape=undefined)},_listen:function(){var a=this;this.content.addEventListener("mousedown",function(b){a.mouseDown=!0,a._handleStageEvent(b)},!1),this.content.addEventListener("mousemove",function(b){a.mouseUp=!1,a.mouseDown=!1,a._handleStageEvent(b)},!1),this.content.addEventListener("mouseup",function(b){a.mouseUp=!0,a.mouseDown=!1,a._handleStageEvent(b),a.clickStart=!1},!1),this.content.addEventListener("mouseover",function(b){a._handleStageEvent(b)},!1),this.content.addEventListener("mouseout",function(b){var c=a.targetShape;c&&c._handleEvents("onmouseout",b),a.mousePos=undefined},!1),this.content.addEventListener("touchstart",function(b){b.preventDefault(),a.touchStart=!0,a._handleStageEvent(b)},!1),this.content.addEventListener("touchmove",function(b){b.preventDefault(),a._handleStageEvent(b)},!1),this.content.addEventListener("touchend",function(b){b.preventDefault(),a.touchEnd=!0,a._handleStageEvent(b)},!1)},_setMousePosition:function(a){var b=a.offsetX||a.clientX-this._getContentPosition().left+window.pageXOffset,c=a.offsetY||a.clientY-this._getContentPosition().top+window.pageYOffset;this.mousePos={x:b,y:c}},_setTouchPosition:function(a){if(a.touches!==undefined&&a.touches.length===1){var b=a.touches[0],c=b.clientX-this._getContentPosition().left+window.pageXOffset,d=b.clientY-this._getContentPosition().top+window.pageYOffset;this.touchPos={x:c,y:d}}},_getContentPosition:function(){var a=this.content,b=0,c=0;while(a&&a.tagName!=="BODY")b+=a.offsetTop-a.scrollTop,c+=a.offsetLeft-a.scrollLeft,a=a.offsetParent;return{top:b,left:c}},_modifyPathContext:function(a){a.stroke=function(){},a.fill=function(){},a.fillRect=function(a,b,c,d){layer.context.rect(a,b,c,d)},a.strokeRect=function(a,b,c,d){layer.context.rect(a,b,c,d)},a.drawImage=function(){},a.fillText=function(){},a.strokeText=function(){}},_endDrag:function(a){var b=Kinetic.GlobalObject;b.drag.node&&b.drag.moving&&(b.drag.moving=!1,b.drag.node._handleEvents("ondragend",a)),b.drag.node=undefined},_prepareDrag:function(){var a=this;this.onContent("mousemove touchmove",function(b){var c=Kinetic.GlobalObject,d=c.drag.node;if(d){var e=a.getUserPosition(),f=d.dragConstraint,g=d.dragBounds,h={x:e.x-c.drag.offset.x,y:e.y-c.drag.offset.y};g.left!==undefined&&h.xg.right&&(h.x=g.right),g.top!==undefined&&h.yg.bottom&&(h.y=g.bottom);var i=d.rotation,j={x:d.scale.x,y:d.scale.y};d.rotation=0,d.scale={x:1,y:1};var k=d.getAbsoluteTransform();k.invert(),k.translate(h.x,h.y),h={x:d.x+k.getTranslation().x,y:d.y+k.getTranslation().y},f==="horizontal"?h.y=d.y:f==="vertical"&&(h.x=d.x),d.setPosition(h.x,h.y),d.rotate(i),d.scale={x:j.x,y:j.y},c.drag.node.getLayer().draw(),c.drag.moving||(c.drag.moving=!0,c.drag.node._handleEvents("ondragstart",b)),c.drag.node._handleEvents("ondragmove",b)}},!1),this.onContent("mouseup touchend mouseout",function(b){a._endDrag(b)})},_buildDOM:function(){this.content.style.width=this.width+"px",this.content.style.height=this.height+"px",this.content.style.position="relative",this.content.style.display="inline-block",this.content.className="kineticjs-content",this.container.appendChild(this.content),this.bufferLayer=new Kinetic.Layer({name:"bufferLayer"}),this.pathLayer=new Kinetic.Layer({name:"pathLayer"}),this.bufferLayer.parent=this,this.pathLayer.parent=this,this._modifyPathContext(this.pathLayer.context),this.bufferLayer.getCanvas().style.display="none",this.pathLayer.getCanvas().style.display="none",this.bufferLayer.canvas.width=this.width,this.bufferLayer.canvas.height=this.height,this.bufferLayer.canvas.className="kineticjs-buffer-layer",this.content.appendChild(this.bufferLayer.canvas),this.pathLayer.canvas.width=this.width,this.pathLayer.canvas.height=this.height,this.pathLayer.canvas.className="kineticjs-path-layer",this.content.appendChild(this.pathLayer.canvas)}},Kinetic.GlobalObject.extend(Kinetic.Stage,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Stage,Kinetic.Node),Kinetic.Layer=function(a){this.className="Layer",this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),this.canvas.style.position="absolute",Kinetic.Container.apply(this,[]),Kinetic.Node.apply(this,[a])},Kinetic.Layer.prototype={draw:function(){this._draw()},clear:function(){var a=this.getContext(),b=this.getCanvas();a.clearRect(0,0,b.width,b.height)},getCanvas:function(){return this.canvas},getContext:function(){return this.context},add:function(a){this._add(a)},remove:function(a){this._remove(a)},_draw:function(){this.clear(),this.visible&&this._drawChildren()}},Kinetic.GlobalObject.extend(Kinetic.Layer,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Layer,Kinetic.Node),Kinetic.Group=function(a){this.className="Group",Kinetic.Container.apply(this,[]),Kinetic.Node.apply(this,[a])},Kinetic.Group.prototype={add:function(a){this._add(a)},remove:function(a){this._remove(a)},_draw:function(){this.visible&&this._drawChildren()}},Kinetic.GlobalObject.extend(Kinetic.Group,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Group,Kinetic.Node),Kinetic.Shape=function(a){this.className="Shape",this.data=[];if(a.stroke!==undefined||a.strokeWidth!==undefined)a.stroke===undefined?a.stroke="black":a.strokeWidth===undefined&&(a.strokeWidth=2);this.detectionType="path",this.drawFunc=a.drawFunc,Kinetic.Node.apply(this,[a])},Kinetic.Shape.prototype={getContext:function(){return this.tempLayer.getContext()},getCanvas:function(){return this.tempLayer.getCanvas()},fillStroke:function(){var a=this.getContext();this.fill!==undefined&&(a.fillStyle=this.fill,a.fill()),this.stroke!==undefined&&(a.lineWidth=this.strokeWidth===undefined?1:this.strokeWidth,a.strokeStyle=this.stroke,a.stroke())},applyLineJoin:function(){var a=this.getContext();this.lineJoin!==undefined&&(a.lineJoin=this.lineJoin)},setFill:function(a){this.fill=a},getFill:function(){return this.fill},setStroke:function(a){this.stroke=a},getStroke:function(){return this.stroke},setLineJoin:function(a){this.lineJoin=a},getLineJoin:function(){return this.lineJoin},setStrokeWidth:function(a){this.strokeWidth=a},getStrokeWidth:function(){return this.strokeWidth},setDrawFunc:function(a){this.drawFunc=a},save:function(){var a=this.getStage(),b=a.width,c=a.height,d=a.bufferLayer,e=d.getContext();d.clear(),this._draw(d);var f=e.getImageData(0,0,b,c);this.data=f.data},_draw:function(a){if(this.visible){var b=a.getStage(),c=a.getContext(),d=[],e=this.parent;d.unshift(this);while(e)d.unshift(e),e=e.parent;c.save();for(var f=0;fthis.getDuration()?this.looping?(this.rewind(a-this._duration),this.update(),this.broadcastMessage("onTweenLooped",{target:this,type:"onTweenLooped"})):(this._time=this._duration,this.update(),this.stop(),this.broadcastMessage("onTweenFinished",{target:this,type:"onTweenFinished"})):a<0?(this.rewind(),this.update()):(this._time=a,this.update())},getTime:function(){return this._time},setDuration:function(a){this._duration=a==null||a<=0?1e5:a},getDuration:function(){return this._duration},setPosition:function(a){this.prevPos=this._pos,this.propFunc(a),this._pos=a,this.broadcastMessage("onTweenChanged",{target:this,type:"onTweenChanged"})},getPosition:function(a){return a==undefined&&(a=this._time),this.func(a,this.begin,this._change,this._duration)},setFinish:function(a){this._change=a-this.begin},getFinish:function(){return this.begin+this._change},start:function(){this.rewind(),this.startEnterFrame(),this.broadcastMessage("onTweenStarted",{target:this,type:"onTweenStarted"})},rewind:function(a){this.stop(),this._time=a==undefined?0:a,this.fixTime(),this.update()},fforward:function(){this._time=this._duration,this.fixTime(),this.update()},update:function(){this.setPosition(this.getPosition(this._time))},startEnterFrame:function(){this.stopEnterFrame(),this.isPlaying=!0,this.onEnterFrame()},onEnterFrame:function(){this.isPlaying&&this.nextFrame()},nextFrame:function(){this.setTime((this.getTimer()-this._startTime)/1e3)},stop:function(){this.stopEnterFrame(),this.broadcastMessage("onTweenStopped",{target:this,type:"onTweenStopped"})},stopEnterFrame:function(){this.isPlaying=!1},continueTo:function(a,b){this.begin=this._pos,this.setFinish(a),this._duration!=undefined&&this.setDuration(b),this.start()},resume:function(){this.fixTime(),this.startEnterFrame(),this.broadcastMessage("onTweenResumed",{target:this,type:"onTweenResumed"})},yoyo:function(){this.continueTo(this.begin,this._time)},addListener:function(a){return this.removeListener(a),this._listeners.push(a)},removeListener:function(a){var b=this._listeners,c=b.length;while(c--)if(b[c]==a)return b.splice(c,1),!0;return!1},broadcastMessage:function(){var a=new Array;for(var b=0;b 0) { - return true; - } - } - } - - this.frame.lastTime = 0; - return false; - }, - _endTransition: function() { - var config = this.config; - for(var key in config) { - if(config.hasOwnProperty(key)) { - if(config[key].x !== undefined || config[key].y !== undefined) { - var propArray = ['x', 'y']; - for(var n = 0; n < propArray.length; n++) { - var prop = propArray[n]; - if(config[key][prop] !== undefined) { - this.node[key][prop] = config[key][prop]; - } - } - } - else { - this.node[key] = config[key]; - } - } - } - }, - _transitionPow: function(transition, key, prop, powFunc) { - var pow = powFunc(); - - var config = transition.config; - var timeDiff = this.frame.timeDiff; - if(prop !== undefined) { - var start = transition.starts[key][prop]; - var change = config[key][prop] - start; - var b = change / (Math.pow(config.duration * 1000, pow)); - transition.node[key][prop] = b * Math.pow(transition.time, pow) + start; - } - else { - var start = transition.starts[key]; - var change = config[key] - start; - var b = change / (Math.pow(config.duration * 1000, pow)); - transition.node[key] = b * Math.pow(transition.time, pow) + start; - } - }, - _chooseTransition: function(transition, key, prop) { - var config = transition.config; - var that = this; - switch(config.easing) { - case 'ease-in': - this._transitionPow(transition, key, prop, function() { - return 2.5; - }); - break; - case 'ease-out': - this._transitionPow(transition, key, prop, function() { - return 0.4; - }); - break; - case 'ease-in-out': - this._transitionPow(transition, key, prop, function() { - var change = -2.1; - var b = change / (config.duration * 1000); - return 2.5 + b * transition.time; - }); - break; - // linear is default - default: - this._transitionPow(transition, key, prop, function() { - return 1; - }); - break; - } - }, - _runTransition: function(transition) { - var config = transition.config; - for(var key in config) { - if(config.hasOwnProperty(key) && key !== 'duration' && key !== 'easing' && key !== 'callback') { - if(config[key].x !== undefined || config[key].y !== undefined) { - var propArray = ['x', 'y']; - for(var n = 0; n < propArray.length; n++) { - var prop = propArray[n]; - if(config[key][prop] !== undefined) { - this._chooseTransition(transition, key, prop); - } - } - } - else { - this._chooseTransition(transition, key); - } - } - } - }, - _clearTransition: function(node) { - var layer = node.getLayer(); - for(var n = 0; n < layer.transitions.length; n++) { - if(layer.transitions[n].node.id === node.id) { - layer.transitions.splice(n, 1); - } - } - }, + var config = transition.config; + var timeDiff = this.frame.timeDiff; + if(prop !== undefined) { + var start = transition.starts[key][prop]; + var change = config[key][prop] - start; + var b = change / (Math.pow(config.duration * 1000, pow)); + transition.node[key][prop] = b * Math.pow(transition.time, pow) + start; + } + else { + var start = transition.starts[key]; + var change = config[key] - start; + var b = change / (Math.pow(config.duration * 1000, pow)); + transition.node[key] = b * Math.pow(transition.time, pow) + start; + } + }, + _chooseTransition: function(transition, key, prop) { + var config = transition.config; + var that = this; + switch(config.easing) { + case 'ease-in': + this._transitionPow(transition, key, prop, function() { + return 2.5; + }); + break; + case 'ease-out': + this._transitionPow(transition, key, prop, function() { + return 0.4; + }); + break; + case 'ease-in-out': + this._transitionPow(transition, key, prop, function() { + var change = -2.1; + var b = change / (config.duration * 1000); + return 2.5 + b * transition.time; + }); + break; + // linear is default + default: + this._transitionPow(transition, key, prop, function() { + return 1; + }); + break; + } + }, + _runTransition: function(transition) { + var config = transition.config; + for(var key in config) { + if(config.hasOwnProperty(key) && key !== 'duration' && key !== 'easing' && key !== 'callback') { + if(config[key].x !== undefined || config[key].y !== undefined) { + var propArray = ['x', 'y']; + for(var n = 0; n < propArray.length; n++) { + var prop = propArray[n]; + if(config[key][prop] !== undefined) { + this._chooseTransition(transition, key, prop); + } + } + } + else { + this._chooseTransition(transition, key); + } + } + } + }, + _clearTransition: function(node) { + var layer = node.getLayer(); + for(var n = 0; n < layer.transitions.length; n++) { + if(layer.transitions[n].node.id === node.id) { + layer.transitions.splice(n, 1); + } + } + }, + */ _runFrames: function() { - for(var n = 0; n < this.stages.length; n++) { - var stage = this.stages[n]; - // run animation if available - if(stage.isAnimating && stage.onFrameFunc !== undefined) { - stage.onFrameFunc(this.frame); - } - - // loop through layers - var layers = stage.getChildren(); - for(var k = 0; k < layers.length; k++) { - var layer = layers[k]; - var didTransition = false; - // loop through transitions - for(var i = 0; i < layer.transitions.length; i++) { - didTransition = true; - var transition = layer.transitions[i]; - transition.time += this.frame.timeDiff; - if(transition.time >= transition.config.duration * 1000) { - this._endTransition.apply(transition); - this._clearTransition(transition.node); - if(transition.config.callback !== undefined) { - transition.config.callback(); - } - } - else { - this._runTransition(transition); - } - } - - if(didTransition) { - layer.draw(); - } - } + for(var n = 0; n < this.animations.length; n++) { + this.animations[n].func(this.frame); } + /* + for(var n = 0; n < this.stages.length; n++) { + var stage = this.stages[n]; + // run animation if available + if(stage.isAnimating && stage.onFrameFunc !== undefined) { + stage.onFrameFunc(this.frame); + } + + // loop through layers + var layers = stage.getChildren(); + for(var k = 0; k < layers.length; k++) { + var layer = layers[k]; + var didTransition = false; + // loop through transitions + for(var i = 0; i < layer.transitions.length; i++) { + didTransition = true; + var transition = layer.transitions[i]; + transition.time += this.frame.timeDiff; + if(transition.time >= transition.config.duration * 1000) { + this._endTransition.apply(transition); + this._clearTransition(transition.node); + if(transition.config.callback !== undefined) { + transition.config.callback(); + } + } + else { + this._runTransition(transition); + } + } + + if(didTransition) { + layer.draw(); + } + } + } + */ }, _updateFrameObject: function() { var date = new Date(); @@ -193,7 +184,7 @@ Kinetic.GlobalObject = { } }, _animationLoop: function() { - if(this._isaCanvasAnimating()) { + if(this.animations.length > 0) { this._updateFrameObject(); this._runFrames(); var that = this; @@ -201,12 +192,18 @@ Kinetic.GlobalObject = { that._animationLoop(); }); } + else { + this.frame.lastTime = 0; + } }, _handleAnimation: function() { var that = this; - if(this._isaCanvasAnimating()) { + if(this.animations.length > 0) { that._animationLoop(); } + else { + this.frame.lastTime = 0; + } } }; diff --git a/src/Layer.js b/src/Layer.js index 12b0277a..763c11d9 100644 --- a/src/Layer.js +++ b/src/Layer.js @@ -14,8 +14,6 @@ Kinetic.Layer = function(config) { this.canvas = document.createElement('canvas'); this.context = this.canvas.getContext('2d'); this.canvas.style.position = 'absolute'; - this.transitions = []; - this.transitionIdCounter = 0; // call super constructors Kinetic.Container.apply(this, []); diff --git a/src/Node.js b/src/Node.js index 6ca059ab..7fbc7b20 100644 --- a/src/Node.js +++ b/src/Node.js @@ -482,15 +482,19 @@ Kinetic.Node.prototype = { transitionTo: function(config) { var layer = this.getLayer(); var that = this; - var duration = config.duration * 1000; - var starts = {}; + + + //var duration = config.duration * 1000; + //var starts = {}; + //var go = Kinetic.GlobalObject; /* * clear transition if one is currenlty running. * This make it easy to start new transitions without * having to explicitly cancel old ones */ - Kinetic.GlobalObject._clearTransition(this); + /* + go._clearTransition(this); for(var key in config) { if(config.hasOwnProperty(key) && key !== 'duration' && key !== 'easing' && key !== 'callback') { @@ -510,15 +514,16 @@ Kinetic.Node.prototype = { } } - layer.transitions.push({ + go.transitions.push({ id: layer.transitionIdCounter++, time: 0, config: config, node: this, starts: starts }); + */ - Kinetic.GlobalObject._handleAnimation(); + go._handleAnimation(); }, /** * set drag constraint diff --git a/src/Stage.js b/src/Stage.js index 16cd873a..6f662c64 100644 --- a/src/Stage.js +++ b/src/Stage.js @@ -50,10 +50,6 @@ Kinetic.Stage = function(config) { // set stage id this.id = Kinetic.GlobalObject.idCounter++; - // animation support - this.isAnimating = false; - this.onFrameFunc = undefined; - this._buildDOM(); this._listen(); this._prepareDrag(); @@ -74,20 +70,22 @@ Kinetic.Stage.prototype = { * @param {function} func */ onFrame: function(func) { - this.onFrameFunc = func; + var go = Kinetic.GlobalObject; + go.animations.push({ + id: go.animIdCounter++, + func: func + }); }, /** * start animation */ start: function() { - this.isAnimating = true; Kinetic.GlobalObject._handleAnimation(); }, /** * stop animation */ stop: function() { - this.isAnimating = false; Kinetic.GlobalObject._handleAnimation(); }, /** diff --git a/src/Transitions.js b/src/Transitions.js new file mode 100644 index 00000000..0f7c0f2e --- /dev/null +++ b/src/Transitions.js @@ -0,0 +1,335 @@ +Kinetic.Transition = function(obj, propFunc, func, begin, finish, duration) { + this._listeners = []; + this.addListener(this); + this.obj = obj; + this.propFunc = propFunc; + this.begin = begin; + this._pos = begin; + this.setDuration(duration); + + this.isPlaying = false; + + this._change = 0; + + this.prevTime = 0; + this.prevPos = 0; + this.looping = false; + + this._time = 0; + this._position = 0; + this._startTime = 0; + this._finish = 0; + + this.name = ''; + + //set the tweening function + if(func !== null && func !== '') { + this.func = func; + } + else { + this.func = function(t, b, c, d) { + return c * t / d + b; + }; + } + + this.setFinish(finish); +}; + +Kinetic.Transition.prototype = { + setTime: function(t) { + this.prevTime = this._time; + if(t > this.getDuration()) { + if(this.looping) { + this.rewind(t - this._duration); + this.update(); + this.broadcastMessage('onTweenLooped', { + target: this, + type: 'onTweenLooped' + }); + } + else { + this._time = this._duration; + this.update(); + this.stop(); + this.broadcastMessage('onTweenFinished', { + target: this, + type: 'onTweenFinished' + }); + } + } + else if(t < 0) { + this.rewind(); + this.update(); + } + else { + this._time = t; + this.update(); + } + }, + getTime: function() { + return this._time; + }, + setDuration: function(d) { + this._duration = (d == null || d <= 0) ? 100000 : d; + }, + getDuration: function() { + return this._duration; + }, + setPosition: function(p) { + this.prevPos = this._pos; + //var a = this.suffixe != '' ? this.suffixe : ''; + this.propFunc(p); + //+ a; + //this.obj(Math.round(p)); + this._pos = p; + this.broadcastMessage('onTweenChanged', { + target: this, + type: 'onTweenChanged' + }); + }, + getPosition: function(t) { + if(t == undefined) + t = this._time; + return this.func(t, this.begin, this._change, this._duration); + }, + setFinish: function(f) { + this._change = f - this.begin; + }, + getFinish: function() { + return this.begin + this._change; + }, + start: function() { + this.rewind(); + this.startEnterFrame(); + this.broadcastMessage('onTweenStarted', { + target: this, + type: 'onTweenStarted' + }); + }, + rewind: function(t) { + this.stop(); + this._time = (t == undefined) ? 0 : t; + this.fixTime(); + this.update(); + }, + fforward: function() { + this._time = this._duration; + this.fixTime(); + this.update(); + }, + update: function() { + this.setPosition(this.getPosition(this._time)); + }, + startEnterFrame: function() { + this.stopEnterFrame(); + this.isPlaying = true; + this.onEnterFrame(); + }, + onEnterFrame: function() { + if(this.isPlaying) + this.nextFrame(); + }, + nextFrame: function() { + this.setTime((this.getTimer() - this._startTime) / 1000); + }, + stop: function() { + this.stopEnterFrame(); + this.broadcastMessage('onTweenStopped', { + target: this, + type: 'onTweenStopped' + }); + }, + stopEnterFrame: function() { + this.isPlaying = false; + }, + continueTo: function(finish, duration) { + this.begin = this._pos; + this.setFinish(finish); + if(this._duration != undefined) + this.setDuration(duration); + this.start(); + }, + resume: function() { + this.fixTime(); + this.startEnterFrame(); + this.broadcastMessage('onTweenResumed', { + target: this, + type: 'onTweenResumed' + }); + }, + yoyo: function() { + this.continueTo(this.begin, this._time); + }, + addListener: function(o) { + this.removeListener(o); + return this._listeners.push(o); + }, + removeListener: function(o) { + var a = this._listeners; + var i = a.length; + while(i--) { + if(a[i] == o) { + a.splice(i, 1); + return true; + } + } + return false; + }, + broadcastMessage: function() { + var arr = new Array(); + for(var i = 0; i < arguments.length; i++) { + arr.push(arguments[i]) + } + var e = arr.shift(); + var a = this._listeners; + var l = a.length; + for(var i = 0; i < l; i++) { + if(a[i][e]) + a[i][e].apply(a[i], arr); + } + }, + fixTime: function() { + this._startTime = this.getTimer() - this._time * 1000; + }, + getTimer: function() { + return new Date().getTime() - this._time; + } +}; + +Kinetic.Transitions = { + backEaseIn: function(t, b, c, d, a, p) { + var s = 1.70158; + return c * (t /= d) * t * ((s + 1) * t - s) + b; + }, + backEaseOut: function(t, b, c, d, a, p) { + var s = 1.70158; + return c * (( t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; + }, + backEaseInOut: function(t, b, c, d, a, p) { + var s = 1.70158; + if((t /= d / 2) < 1) { + return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b; + } + return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b; + }, + elasticEaseIn: function(t, b, c, d, a, p) { + // added s = 0 + var s = 0; + if(t === 0) { + return b; + } + if((t /= d) == 1) { + return b + c; + } + if(!p) { + p = d * 0.3; + } + if(!a || a < Math.abs(c)) { + a = c; + s = p / 4; + } + else { + s = p / (2 * Math.PI) * Math.asin(c / a); + } + return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; + }, + elasticEaseOut: function(t, b, c, d, a, p) { + // added s = 0 + var s = 0; + if(t === 0) { + return b; + } + if((t /= d) == 1) { + return b + c; + } + if(!p) { + p = d * 0.3; + } + if(!a || a < Math.abs(c)) { + a = c; + s = p / 4; + } + else { + s = p / (2 * Math.PI) * Math.asin(c / a); + } + return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b); + }, + elasticEaseInOut: function(t, b, c, d, a, p) { + var s = 0; + if(t === 0) { + return b; + } + if((t /= d / 2) == 2) { + return b + c; + } + if(!p) { + p = d * (0.3 * 1.5); + } + if(!a || a < Math.abs(c)) { + a = c; + s = p / 4; + } + else { + s = p / (2 * Math.PI) * Math.asin(c / a); + } + if(t < 1) { + return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; + } + return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b; + }, + bounceEaseOut: function(t, b, c, d) { + if((t /= d) < (1 / 2.75)) { + return c * (7.5625 * t * t) + b; + } + else if(t < (2 / 2.75)) { + return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b; + } + else if(t < (2.5 / 2.75)) { + return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b; + } + else { + return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b; + } + }, + bounceEaseIn: function(t, b, c, d) { + return c - Kinetic.Transitions.bounceEaseOut(d - t, 0, c, d) + b; + }, + bounceEaseInOut: function(t, b, c, d) { + if(t < d / 2) { + return Kinetic.Transitions.bounceEaseIn(t * 2, 0, c, d) * 0.5 + b; + } + else { + return Kinetic.Transitions.bounceEaseOut(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; + } + }, + // duplicate + /* + strongEaseInOut: function(t, b, c, d) { + return c * (t /= d) * t * t * t * t + b; + }, + */ + regularEaseIn: function(t, b, c, d) { + return c * (t /= d) * t + b; + }, + regularEaseOut: function(t, b, c, d) { + return -c * (t /= d) * (t - 2) + b; + }, + regularEaseInOut: function(t, b, c, d) { + if((t /= d / 2) < 1) { + return c / 2 * t * t + b; + } + return -c / 2 * ((--t) * (t - 2) - 1) + b; + }, + strongEaseIn: function(t, b, c, d) { + return c * (t /= d) * t * t * t * t + b; + }, + strongEaseOut: function(t, b, c, d) { + return c * (( t = t / d - 1) * t * t * t * t + 1) + b; + }, + strongEaseInOut: function(t, b, c, d) { + if((t /= d / 2) < 1) { + return c / 2 * t * t * t * t * t + b; + } + return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; + } +}; diff --git a/tests/js/Test.js b/tests/js/Test.js index 6c3a91ac..d504f270 100644 --- a/tests/js/Test.js +++ b/tests/js/Test.js @@ -10,7 +10,7 @@ function log(message) { * Test constructor */ function Test() { - this.testOnly = ''; + this.testOnly = 'TRANSITION - transition position and rotation'; this.counter = 0; } /** diff --git a/tests/js/functionalTests.js b/tests/js/functionalTests.js index 3caaedc5..88b6cfa1 100644 --- a/tests/js/functionalTests.js +++ b/tests/js/functionalTests.js @@ -19,12 +19,26 @@ Test.prototype.tests = { layer.add(rect); stage.add(layer); - rect.transitionTo({ - x: 400, - y: 30, - rotation: Math.PI * 2, - duration: 1 + var tween = new Kinetic.Transition(rect, function(i) { + rect.setScale(i); + }, Kinetic.Transitions.bounceEaseOut, 10, 1, 2); + + stage.onFrame(function(frame) { + tween.onEnterFrame(); + layer.draw(); }); + + tween.start(); + stage.start(); + + /* + rect.transitionTo({ + x: 400, + y: 30, + rotation: Math.PI * 2, + duration: 1 + }); + */ }, 'TRANSITION - transition position and rotation with two transitions': function(containerId) { var stage = new Kinetic.Stage({