Форк Rambox
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

227 lines
6.2 KiB

/**
* @class Ext.draw.Animator
*
* Singleton class that manages the animation pool.
*/
Ext.define('Ext.draw.Animator', {
uses: ['Ext.draw.Draw'],
singleton: true,
frameCallbacks: {},
frameCallbackId: 0,
scheduled: 0,
frameStartTimeOffset: Ext.now(),
animations: [],
running: false,
/**
* Cross platform `animationTime` implementation.
* @return {Number}
*/
animationTime: function () {
return Ext.AnimationQueue.frameStartTime - this.frameStartTimeOffset;
},
/**
* Adds an animated object to the animation pool.
*
* @param {Object} animation The animation descriptor to add to the pool.
*/
add: function (animation) {
var me = this;
if (!me.contains(animation)) {
me.animations.push(animation);
me.ignite();
if ('fireEvent' in animation) {
animation.fireEvent('animationstart', animation);
}
}
},
/**
* Removes an animation from the pool.
* TODO: This is broken when called within `step` method.
* @param {Object} animation The animation to remove from the pool.
*/
remove: function (animation) {
var me = this,
animations = me.animations,
i = 0,
l = animations.length;
for (; i < l; ++i) {
if (animations[i] === animation) {
animations.splice(i, 1);
if ('fireEvent' in animation) {
animation.fireEvent('animationend', animation);
}
return;
}
}
},
/**
* Returns `true` or `false` whether it contains the given animation or not.
*
* @param {Object} animation The animation to check for.
* @return {Boolean}
*/
contains: function (animation) {
return Ext.Array.indexOf(this.animations, animation) > -1;
},
/**
* Returns `true` or `false` whether the pool is empty or not.
* @return {Boolean}
*/
empty: function () {
return this.animations.length === 0;
},
/**
* Given a frame time it will filter out finished animations from the pool.
*
* @param {Number} frameTime The frame's start time, in milliseconds.
*/
step: function (frameTime) {
var me = this,
animations = me.animations,
animation,
i = 0,
ln = animations.length;
for (; i < ln; i++) {
animation = animations[i];
animation.step(frameTime);
if (!animation.animating) {
animations.splice(i, 1);
i--;
ln--;
if (animation.fireEvent) {
animation.fireEvent('animationend');
}
}
}
},
/**
* Register a one-time callback that will be called at the next frame.
* @param {Function/String} callback
* @param {Object} scope
* @return {String} The ID of the scheduled callback.
*/
schedule: function (callback, scope) {
scope = scope || this;
var id = 'frameCallback' + (this.frameCallbackId++);
if (Ext.isString(callback)) {
callback = scope[callback];
}
Ext.draw.Animator.frameCallbacks[id] = {fn: callback, scope: scope, once: true};
this.scheduled++;
Ext.draw.Animator.ignite();
return id;
},
/**
* Register a one-time callback that will be called at the next frame,
* if that callback (with a matching function and scope) isn't already scheduled.
* @param {Function/String} callback
* @param {Object} scope
* @return {String/null} The ID of the scheduled callback or null, if that callback has already been scheduled.
*/
scheduleIf: function (callback, scope) {
scope = scope || this;
var frameCallbacks = Ext.draw.Animator.frameCallbacks,
cb, id;
if (Ext.isString(callback)) {
callback = scope[callback];
}
for (id in frameCallbacks) {
cb = frameCallbacks[id];
if (cb.once && cb.fn === callback && cb.scope === scope) {
return null;
}
}
return this.schedule(callback, scope);
},
/**
* Cancel a registered one-time callback
* @param {String} id
*/
cancel: function (id) {
if (Ext.draw.Animator.frameCallbacks[id] && Ext.draw.Animator.frameCallbacks[id].once) {
this.scheduled--;
delete Ext.draw.Animator.frameCallbacks[id];
}
},
/**
* Register a recursive callback that will be called at every frame.
*
* @param {Function} callback
* @param {Object} scope
* @return {String}
*/
addFrameCallback: function (callback, scope) {
scope = scope || this;
if (Ext.isString(callback)) {
callback = scope[callback];
}
var id = 'frameCallback' + (this.frameCallbackId++);
Ext.draw.Animator.frameCallbacks[id] = {fn: callback, scope: scope};
return id;
},
/**
* Unregister a recursive callback.
* @param {String} id
*/
removeFrameCallback: function (id) {
delete Ext.draw.Animator.frameCallbacks[id];
},
/**
* @private
*/
fireFrameCallbacks: function () {
var callbacks = this.frameCallbacks,
id, fn, cb;
for (id in callbacks) {
cb = callbacks[id];
fn = cb.fn;
if (Ext.isString(fn)) {
fn = cb.scope[fn];
}
fn.call(cb.scope);
if (callbacks[id] && cb.once) {
this.scheduled--;
delete callbacks[id];
}
}
},
handleFrame: function() {
this.step(this.animationTime());
this.fireFrameCallbacks();
if (!this.scheduled && this.empty()) {
Ext.AnimationQueue.stop(this.handleFrame, this);
this.running = false;
}
},
ignite: function() {
if (!this.running) {
this.running = true;
Ext.AnimationQueue.start(this.handleFrame, this);
Ext.draw.Draw.updateIOS();
}
}
});