File:SpineInstance.js
/**
* @module PIXI Spine
* @namespace springroll.pixi
* @requires Core, PIXI Display, Animation
*/
(function(undefined)
{
var Application = include("springroll.Application");
var AnimatorInstance = include('springroll.AnimatorInstance');
var Spine = include('PIXI.spine.Spine', false);
var ParallelSpineData = include('springroll.pixi.ParallelSpineData');
if (!Spine) return;
/**
* The plugin for working with Spine skeletons and animator
* @class SpineInstance
* @extends springroll.AnimatorInstance
* @private
*/
var SpineInstance = function()
{
AnimatorInstance.call(this);
this.prevPosition = 0;
};
// Reference to the prototype
var p = AnimatorInstance.extend(SpineInstance);
/**
* The initialization method
* @method init
* @param {*} clip The movieclip
*/
p.init = function(clip)
{
//we don't want Spine animations to advance every render, only when Animator tells them to
clip.autoUpdate = false;
this.clip = clip;
this.isLooping = false;
this.currentName = null;
this.position = this.duration = 0;
};
p.beginAnim = function(animObj, isRepeat)
{
var spineState = this.clip.state;
spineState.clearTracks();
var skeletonData = this.clip.stateData.skeletonData;
this.isLooping = !!animObj.loop;
var anim = this.currentName = animObj.anim;
if (typeof anim == "string")
{
//single anim
this.duration = skeletonData.findAnimation(anim).duration;
spineState.setAnimationByName(0, anim, this.isLooping);
}
else //if(Array.isArray(anim))
{
var i;
//concurrent spine anims
if (anim[0] instanceof ParallelSpineData)
{
//this.spineSpeeds = new Array(anim.length);
this.duration = 0;
var maxDuration = 0,
maxLoopDuration = 0,
duration;
for (i = 0; i < anim.length; ++i)
{
var animLoop = anim[i].loop;
spineState.setAnimationByName(i, anim[i].anim, animLoop);
duration = skeletonData.findAnimation(anim[i].anim).duration;
if (animLoop)
{
if (duration > maxLoopDuration)
maxLoopDuration = duration;
}
else
{
if (duration > maxDuration)
maxDuration = duration;
}
/*if (anim[i].speed > 0)
this.spineSpeeds[i] = anim[i].speed;
else
this.spineSpeeds[i] = 1;*/
}
//set the duration to be the longest of the non looping animations
//or the longest loop if they all loop
this.duration = maxDuration || maxLoopDuration;
}
//list of sequential spine anims
else
{
this.duration = skeletonData.findAnimation(anim[0]).duration;
if (anim.length == 1)
{
spineState.setAnimationByName(0, anim[0], this.isLooping);
}
else
{
spineState.setAnimationByName(0, anim[0], false);
for (i = 1; i < anim.length; ++i)
{
spineState.addAnimationByName(0, anim[i],
this.isLooping && i == anim.length - 1);
this.duration += skeletonData.findAnimation(anim[i]).duration;
}
}
}
}
if (isRepeat)
this.position = 0;
else
{
var animStart = animObj.start || 0;
this.position = animStart < 0 ? Math.random() * this.duration : animStart;
}
this.clip.update(this.position);
};
/**
* Ends animation playback.
* @method endAnim
*/
p.endAnim = function()
{
this.clip.update(this.duration - this.position);
};
/**
* Updates position to a new value, and does anything that the clip needs, like updating
* timelines.
* @method setPosition
* @param {Number} newPos The new position in the animation.
*/
p.setPosition = function(newPos)
{
if (newPos < this.position)
this.clip.update(this.duration - this.position + newPos);
else
this.clip.update(newPos - this.position);
this.position = newPos;
};
/**
* Check to see if a clip is compatible with this
* @method test
* @static
* @return {Boolean} if the clip is supported by this instance
*/
SpineInstance.test = function(clip)
{
return clip instanceof Spine;
};
/**
* Checks if animation exists
*
* @method hasAnimation
* @static
* @param {*} clip The clip to check for an animation.
* @param {String} event The frame label event (e.g. "onClose" to "onClose_stop")
* @return {Boolean} does this animation exist?
*/
SpineInstance.hasAnimation = function(clip, anim)
{
var i;
var skeletonData = clip.stateData.skeletonData;
if (typeof anim == "string")
{
//single anim
return !!skeletonData.findAnimation(anim);
}
else if (Array.isArray(anim))
{
//concurrent spine anims
if (anim[0] instanceof ParallelSpineData)
{
for (i = 0; i < anim.length; ++i)
{
//ensure all animations exist
if (!skeletonData.findAnimation(anim[i].anim))
return false;
}
}
//list of sequential spine anims
else
{
for (i = 0; i < anim.length; ++i)
{
//ensure all animations exist
if (!skeletonData.findAnimation(anim[i]))
return false;
}
}
return true;
}
return false;
};
/**
* Calculates the duration of an animation or list of animations.
* @method getDuration
* @static
* @param {*} clip The clip to check.
* @param {String} event The animation or animation list.
* @return {Number} Animation duration in milliseconds.
*/
SpineInstance.getDuration = function(clip, event)
{
var i;
var skeletonData = this.clip.stateData.skeletonData;
if (typeof anim == "string")
{
//single anim
return skeletonData.findAnimation(anim).duration;
}
else if (Array.isArray(anim))
{
var duration = 0;
//concurrent spine anims
if (anim[0] instanceof ParallelSpineData)
{
var maxDuration = 0,
maxLoopDuration = 0,
tempDur;
for (i = 0; i < anim.length; ++i)
{
var animLoop = anim[i].loop;
tempDur = skeletonData.findAnimation(anim[i].anim).duration;
if (animLoop)
{
if (tempDur > maxLoopDuration)
maxLoopDuration = tempDur;
}
else
{
if (tempDur > maxDuration)
maxDuration = tempDur;
}
}
//set the duration to be the longest of the non looping animations
//or the longest loop if they all loop
duration = maxDuration || maxLoopDuration;
}
//list of sequential spine anims
else
{
duration = skeletonData.findAnimation(anim[0]).duration;
if (anim.length > 1)
{
for (i = 1; i < anim.length; ++i)
{
duration += skeletonData.findAnimation(anim[i]).duration;
}
}
}
return duration;
}
return 0;
};
/**
* Reset this animator instance
* so it can be re-used.
* @method destroy
*/
p.destroy = function()
{
this.clip = null;
};
// Assign to namespace
namespace('springroll.pixi').SpineInstance = SpineInstance;
}());