/**
* Hilo
* Copyright 2015 alibaba.com
* Licensed under the MIT License
*/
/**
*
*
* @class 动画精灵类。
* @augments View
* @module hilo/view/Sprite
* @requires hilo/core/Hilo
* @requires hilo/core/Class
* @requires hilo/view/View
* @requires hilo/view/Drawable
* @param properties 创建对象的属性参数。可包含此类所有可写属性。此外还包括:
*
* @property {number} currentFrame 当前播放帧的索引。从0开始。只读属性。
* @property {boolean} paused 判断精灵是否暂停。默认为false。
* @property {boolean} loop 判断精灵是否可以循环播放。默认为true。
* @property {boolean} timeBased 指定精灵动画是否是以时间为基准。默认为false,即以帧为基准。
* @property {number} interval 精灵动画的帧间隔。如果timeBased为true,则单位为毫秒,否则为帧数。
*/
var Sprite = Class.create(/** @lends Sprite.prototype */{
Extends: View,
constructor: function(properties){
properties = properties || {};
this.id = this.id || properties.id || Hilo.getUid("Sprite");
Sprite.superclass.constructor.call(this, properties);
this._frames = [];
this._frameNames = {};
this.drawable = new Drawable();
if(properties.frames) this.addFrame(properties.frames);
},
_frames: null, //所有帧的集合 Collection of all frames
_frameNames: null, //带名字name的帧的集合 Collection of frames that with name
_frameElapsed: 0, //当前帧持续的时间或帧数 Elapsed time of current frame.
_firstRender: true, //标记是否是第一次渲染 Is the first render.
paused: false,
loop: true,
timeBased: false,
interval: 1,
currentFrame: 0, //当前帧的索引 Index of current frame
/**
* 返回精灵动画的总帧数。
* @returns {Uint} 精灵动画的总帧数。
*/
getNumFrames: function(){
return this._frames ? this._frames.length : 0;
},
/**
* 往精灵动画序列中增加帧。
* @param {Object} frame 要增加的精灵动画帧数据。
* @param {Int} startIndex 开始增加帧的索引位置。若不设置,默认为在末尾添加。
* @returns {Sprite} Sprite对象本身。
*/
addFrame: function(frame, startIndex){
var start = startIndex != null ? startIndex : this._frames.length;
if(frame instanceof Array){
for(var i = 0, len = frame.length; i < len; i++){
this.setFrame(frame[i], start + i);
}
}else{
this.setFrame(frame, start);
}
return this;
},
/**
* 设置精灵动画序列指定索引位置的帧。
* @param {Object} frame 要设置的精灵动画帧数据。
* @param {Int} index 要设置的索引位置。
* @returns {Sprite} Sprite对象本身。
*/
setFrame: function(frame, index){
var frames = this._frames,
total = frames.length;
index = index < 0 ? 0 : index > total ? total : index;
frames[index] = frame;
if(frame.name) this._frameNames[frame.name] = frame;
if(index == 0 && !this.width || !this.height){
this.width = frame.rect[2];
this.height = frame.rect[3];
}
return this;
},
/**
* 获取精灵动画序列中指定的帧。
* @param {Object} indexOrName 要获取的帧的索引位置或别名。
* @returns {Object} 精灵帧对象。
*/
getFrame: function(indexOrName){
if(typeof indexOrName === 'number'){
var frames = this._frames;
if(indexOrName < 0 || indexOrName >= frames.length) return null;
return frames[indexOrName];
}
return this._frameNames[indexOrName];
},
/**
* 获取精灵动画序列中指定帧的索引位置。
* @param {Object} frameValue 要获取的帧的索引位置或别名。
* @returns {Object} 精灵帧对象。
*/
getFrameIndex: function(frameValue){
var frames = this._frames,
total = frames.length,
index = -1;
if(typeof frameValue === 'number'){
index = frameValue;
}else{
var frame = typeof frameValue === 'string' ? this._frameNames[frameValue] : frameValue;
if(frame){
for(var i = 0; i < total; i++){
if(frame === frames[i]){
index = i;
break;
}
}
}
}
return index;
},
/**
* 播放精灵动画。
* @returns {Sprite} Sprite对象本身。
*/
play: function(){
this.paused = false;
return this;
},
/**
* 暂停播放精灵动画。
* @returns {Sprite} Sprite对象本身。
*/
stop: function(){
this.paused = true;
return this;
},
/**
* 跳转精灵动画到指定的帧。
* @param {Object} indexOrName 要跳转的帧的索引位置或别名。
* @param {Boolean} pause 指示跳转后是否暂停播放。
* @returns {Sprite} Sprite对象本身。
*/
goto: function(indexOrName, pause){
var total = this._frames.length,
index = this.getFrameIndex(indexOrName);
this.currentFrame = index < 0 ? 0 : index >= total ? total - 1 : index;
this.paused = pause;
this._firstRender = true;
return this;
},
/**
* 渲染方法。
* @private
*/
_render: function(renderer, delta){
var lastFrameIndex = this.currentFrame, frameIndex;
if(this._firstRender){
frameIndex = lastFrameIndex;
this._firstRender = false;
}else{
frameIndex = this._nextFrame(delta);
}
if(frameIndex != lastFrameIndex){
this.currentFrame = frameIndex;
var callback = this._frames[frameIndex].callback;
callback && callback.call(this);
}
//NOTE: it will be deprecated, don't use it.
if(this.onEnterFrame) this.onEnterFrame(frameIndex);
this.drawable.init(this._frames[frameIndex]);
Sprite.superclass._render.call(this, renderer, delta);
},
/**
* @private
*/
_nextFrame: function(delta){
var frames = this._frames,
total = frames.length,
frameIndex = this.currentFrame,
frame = frames[frameIndex],
duration = frame.duration || this.interval,
elapsed = this._frameElapsed;
//calculate the current frame elapsed frames/time
var value = (frameIndex == 0 && !this.drawable) ? 0 : elapsed + (this.timeBased ? delta : 1);
elapsed = this._frameElapsed = value < duration ? value : 0;
if(frame.stop || !this.loop && frameIndex >= total - 1){
this.stop();
}
if(!this.paused && elapsed == 0){
if(frame.next != null){
//jump to the specified frame
frameIndex = this.getFrameIndex(frame.next);
}else if(frameIndex >= total - 1){
//at the end of the frames, go back to first frame
frameIndex = 0;
}else if(this.drawable){
//normal go forward to next frame
frameIndex++;
}
}
return frameIndex;
},
/**
* 设置指定帧的回调函数。即每当播放头进入指定帧时调用callback函数。若callback为空,则会删除回调函数。
* @param {Int|String} frame 要指定的帧的索引位置或别名。
* @param {Function} callback 指定回调函数。
* @returns {Sprite} 精灵本身。
*/
setFrameCallback: function(frame, callback){
frame = this.getFrame(frame);
if(frame) frame.callback = callback;
return this;
},
/**
* 精灵动画的播放头进入新帧时的回调方法。默认值为null。此方法已废弃,请使用addFrameCallback方法。
* @type Function
* @deprecated
*/
onEnterFrame: null
});