greatly improved sprite animation performance by hooking into the global animation object

This commit is contained in:
Eric Rowell 2012-07-07 21:39:03 -07:00
parent a8ab9a2533
commit 30fd5c1fa7
6 changed files with 111 additions and 29 deletions

52
dist/kinetic-core.js vendored
View File

@ -363,12 +363,22 @@ Kinetic.Animation = {
}, },
_runFrames: function() { _runFrames: function() {
var nodes = {}; var nodes = {};
/*
* loop through all animations and execute animation
* function. if the animation object has specified node,
* we can add the node to the nodes hash to eliminate
* drawing the same node multiple times. The node property
* can be the stage itself or a layer
*/
for(var n = 0; n < this.animations.length; n++) { for(var n = 0; n < this.animations.length; n++) {
var anim = this.animations[n]; var anim = this.animations[n];
if(anim.node && anim.node._id !== undefined) { if(anim.node && anim.node._id !== undefined) {
nodes[anim.node._id] = anim.node; nodes[anim.node._id] = anim.node;
} }
anim.func(this.frame); // if animation object has a function, execute it
if(anim.func) {
anim.func(this.frame);
}
} }
for(var key in nodes) { for(var key in nodes) {
@ -1012,9 +1022,9 @@ Kinetic.Node = Kinetic.Class.extend({
* clear transition if one is currently running for this * clear transition if one is currently running for this
* node * node
*/ */
if(this.transAnim !== undefined) { if(this.transAnim) {
a._removeAnimation(this.transAnim); a._removeAnimation(this.transAnim);
this.transAnim = undefined; this.transAnim = null;
} }
/* /*
@ -1043,7 +1053,7 @@ Kinetic.Node = Kinetic.Class.extend({
trans.onFinished = function() { trans.onFinished = function() {
// remove animation // remove animation
a._removeAnimation(anim); a._removeAnimation(anim);
that.transAnim = undefined; that.transAnim = null;
// callback // callback
if(config.callback !== undefined) { if(config.callback !== undefined) {
@ -3677,18 +3687,48 @@ Kinetic.Sprite = Kinetic.Shape.extend({
start: function() { start: function() {
var that = this; var that = this;
var layer = this.getLayer(); var layer = this.getLayer();
var ka = Kinetic.Animation;
// if sprite already has an animation, remove it
if(this.anim) {
ka._removeAnimation(this.anim);
this.anim = null;
}
/*
* animation object has no executable function because
* the updates are done with a fixed FPS with the setInterval
* below. The anim object only needs the layer reference for
* redraw
*/
this.anim = {
node: layer
};
/*
* adding the animation with the addAnimation
* method auto generates an id
*/
ka._addAnimation(this.anim);
this.interval = setInterval(function() { this.interval = setInterval(function() {
that._updateIndex(); that._updateIndex();
layer.draw();
if(that.afterFrameFunc && that.attrs.index === that.afterFrameIndex) { if(that.afterFrameFunc && that.attrs.index === that.afterFrameIndex) {
that.afterFrameFunc(); that.afterFrameFunc();
} }
}, 1000 / this.attrs.frameRate) }, 1000 / this.attrs.frameRate);
ka._handleAnimation();
}, },
/** /**
* stop sprite animation * stop sprite animation
*/ */
stop: function() { stop: function() {
var ka = Kinetic.Animation;
if(this.anim) {
ka._removeAnimation(this.anim);
this.anim = null;
}
clearInterval(this.interval); clearInterval(this.interval);
}, },
/** /**

File diff suppressed because one or more lines are too long

View File

@ -26,12 +26,22 @@ Kinetic.Animation = {
}, },
_runFrames: function() { _runFrames: function() {
var nodes = {}; var nodes = {};
/*
* loop through all animations and execute animation
* function. if the animation object has specified node,
* we can add the node to the nodes hash to eliminate
* drawing the same node multiple times. The node property
* can be the stage itself or a layer
*/
for(var n = 0; n < this.animations.length; n++) { for(var n = 0; n < this.animations.length; n++) {
var anim = this.animations[n]; var anim = this.animations[n];
if(anim.node && anim.node._id !== undefined) { if(anim.node && anim.node._id !== undefined) {
nodes[anim.node._id] = anim.node; nodes[anim.node._id] = anim.node;
} }
anim.func(this.frame); // if animation object has a function, execute it
if(anim.func) {
anim.func(this.frame);
}
} }
for(var key in nodes) { for(var key in nodes) {

View File

@ -591,9 +591,9 @@ Kinetic.Node = Kinetic.Class.extend({
* clear transition if one is currently running for this * clear transition if one is currently running for this
* node * node
*/ */
if(this.transAnim !== undefined) { if(this.transAnim) {
a._removeAnimation(this.transAnim); a._removeAnimation(this.transAnim);
this.transAnim = undefined; this.transAnim = null;
} }
/* /*
@ -622,7 +622,7 @@ Kinetic.Node = Kinetic.Class.extend({
trans.onFinished = function() { trans.onFinished = function() {
// remove animation // remove animation
a._removeAnimation(anim); a._removeAnimation(anim);
that.transAnim = undefined; that.transAnim = null;
// callback // callback
if(config.callback !== undefined) { if(config.callback !== undefined) {

View File

@ -43,18 +43,48 @@ Kinetic.Sprite = Kinetic.Shape.extend({
start: function() { start: function() {
var that = this; var that = this;
var layer = this.getLayer(); var layer = this.getLayer();
var ka = Kinetic.Animation;
// if sprite already has an animation, remove it
if(this.anim) {
ka._removeAnimation(this.anim);
this.anim = null;
}
/*
* animation object has no executable function because
* the updates are done with a fixed FPS with the setInterval
* below. The anim object only needs the layer reference for
* redraw
*/
this.anim = {
node: layer
};
/*
* adding the animation with the addAnimation
* method auto generates an id
*/
ka._addAnimation(this.anim);
this.interval = setInterval(function() { this.interval = setInterval(function() {
that._updateIndex(); that._updateIndex();
layer.draw();
if(that.afterFrameFunc && that.attrs.index === that.afterFrameIndex) { if(that.afterFrameFunc && that.attrs.index === that.afterFrameIndex) {
that.afterFrameFunc(); that.afterFrameFunc();
} }
}, 1000 / this.attrs.frameRate) }, 1000 / this.attrs.frameRate);
ka._handleAnimation();
}, },
/** /**
* stop sprite animation * stop sprite animation
*/ */
stop: function() { stop: function() {
var ka = Kinetic.Animation;
if(this.anim) {
ka._removeAnimation(this.anim);
this.anim = null;
}
clearInterval(this.interval); clearInterval(this.interval);
}, },
/** /**

View File

@ -2068,13 +2068,13 @@ Test.prototype.tests = {
animation: 'standing', animation: 'standing',
animations: anims, animations: anims,
index: 0, index: 0,
//frameRate: Math.random() * 6 + 6, frameRate: Math.random() * 6 + 6,
frameRate: 10, frameRate: 10,
draggable: true, draggable: true,
shadow: { shadow: {
color: 'black', color: 'black',
blur: 3, blur: 3,
offset: [10, 10], offset: [3, 1],
alpha: 0.3 alpha: 0.3
} }
}); });
@ -2086,19 +2086,16 @@ Test.prototype.tests = {
stage.add(layer); stage.add(layer);
// kick once // kick once
/* setTimeout(function() {
setTimeout(function() { sprite.setAnimation('kicking');
sprite.setIndex(0);
sprite.setAnimation('kicking');
sprite.afterFrame(0, function() { sprite.afterFrame(0, function() {
sprite.setAnimation('standing'); sprite.setAnimation('standing');
}); });
}, 2000); }, 2000);
setTimeout(function() { setTimeout(function() {
//sprite.start(); sprite.stop();
}, 3000); }, 3000);
*/
}; };
imageObj.src = '../scorpion-sprite.png'; imageObj.src = '../scorpion-sprite.png';
}, },
@ -3203,7 +3200,7 @@ Test.prototype.tests = {
// make sure private ids are different // make sure private ids are different
test(rect._id !== clone._id, 'rect and clone ids should be different'); test(rect._id !== clone._id, 'rect and clone ids should be different');
// test user event binding cloning // test user event binding cloning
test(clicks.length === 0, 'no clicks should have been triggered yet'); test(clicks.length === 0, 'no clicks should have been triggered yet');
rect.simulate('click'); rect.simulate('click');
test(clicks.toString() === 'myRect', 'only myRect should have been clicked on'); test(clicks.toString() === 'myRect', 'only myRect should have been clicked on');
@ -4535,6 +4532,11 @@ Test.prototype.tests = {
// ANIMATION tests // ANIMATION tests
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
/*
* WARNING: make sure that this is the first unit test that uses
* animation because it's accessing the global animation object which could
* be modified by other unit tests
*/
'ANIMATION - test start and stop': function(containerId) { 'ANIMATION - test start and stop': function(containerId) {
var stage = new Kinetic.Stage({ var stage = new Kinetic.Stage({
container: containerId, container: containerId,