Skip to content

Instantly share code, notes, and snippets.

@thednp
Forked from 6174/createjs-ticker.js
Created September 1, 2016 11:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thednp/d147216834b730f49ae9f40dd7f6071c to your computer and use it in GitHub Desktop.
Save thednp/d147216834b730f49ae9f40dd7f6071c to your computer and use it in GitHub Desktop.
/*
* Ticker by Grant Skinner. Dec 5, 2010
* Visit http://easeljs.com/ for documentation, updates and examples.
*
*
* Copyright (c) 2010 Grant Skinner
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* The Easel Javascript library provides a retained graphics mode for canvas
* including a full, hierarchical display list, a core interaction model, and
* helper classes to make working with Canvas much easier.
* @module EaselJS
**/
(function(window) {
// constructor:
/**
* The Ticker class uses a static interface (ex. Ticker.getPaused()) and should not be instantiated.
* Provides a centralized tick or heartbeat broadcast at a set interval. Listeners can subscribe
* to the tick event to be notified when a set time interval has elapsed.
* Note that the interval that the tick event is called is a target interval, and may be broadcast
* at a slower interval during times of high CPU load.
* @class Ticker
* @static
**/
Ticker = function() {
throw "Ticker cannot be instantiated.";
}
/**
* Event broadcast once each tick / interval. The interval is specified via the
* .setInterval(ms) or setFPS methods.
* @event tick
* @param {Number} timeElapsed The time elapsed in milliseconds since the last tick event.
*/
// private static properties:
/**
* @property _listeners
* @type Array[Object]
* @protected
**/
Ticker._listeners = [];
/**
* @property _pauseable
* @type Array[Boolean]
* @protected
**/
Ticker._pauseable = [];
/**
* @property _paused
* @type Boolean
* @protected
**/
Ticker._paused = false;
/**
* @property _inited
* @type Boolean
* @protected
**/
Ticker._inited = false;
/**
* @property _startTime
* @type Number
* @protected
**/
Ticker._startTime = 0;
/**
* @property _pausedTime
* @type Number
* @protected
**/
Ticker._pausedTime=0;
/**
* Number of ticks that have passed
* @property _ticks
* @type Number
* @protected
**/
Ticker._ticks = 0;
/**
* Number of ticks that have passed while Ticker has been paused
* @property _pausedTickers
* @type Number
* @protected
**/
Ticker._pausedTickers = 0;
/**
* @property _interval
* @type Number
* @protected
**/
Ticker._interval = 50; // READ-ONLY
/**
* @property _intervalID
* @type Number
* @protected
**/
Ticker._intervalID = null;
/**
* @property _lastTime
* @type Number
* @protected
**/
Ticker._lastTime = 0;
/**
* @property _times
* @type Array[Number]
* @protected
**/
Ticker._times = [];
// public static methods:
/**
* Adds a listener for the tick event. The listener object must expose a .tick() method,
* which will be called once each tick / interval. The interval is specified via the
* .setInterval(ms) method.
* The exposed tick method is passed a single parameter, which include the elapsed time between the
* previous tick and the current one.
* @method addListener
* @static
* @param {Object} o The object to add as a listener.
* @param {Boolean} pauseable If false, the listener will continue to have tick called
* even when Ticker is paused via Ticker.pause(). Default is true.
**/
Ticker.addListener = function(o, pauseable) {
if (!Ticker._inited) {
Ticker._inited = true;
Ticker.setInterval(Ticker._interval);
}
this.removeListener(o);
Ticker._pauseable[Ticker._listeners.length] = (pauseable == null) ? true : pauseable;
Ticker._listeners.push(o);
}
/**
* Removes the specified listener.
* @method removeListener
* @static
* @param {Object} o The object to remove from listening from the tick event.
**/
Ticker.removeListener = function(o) {
if (Ticker._listeners == null) { return; }
var index = Ticker._listeners.indexOf(o);
if (index != -1) {
Ticker._listeners.splice(index, 1);
Ticker._pauseable.splice(index, 1);
}
}
/**
* Removes all listeners.
* @method removeAllListeners
* @static
**/
Ticker.removeAllListeners = function() {
Ticker._listeners = [];
Ticker._pauseable = [];
}
/**
* Sets the target time (in milliseconds) between ticks. Default is 50 (20 FPS).
* Note actual time between ticks may be more than requested depending on CPU load.
* @method setInterval
* @static
* @param {Number} interval Time in milliseconds between ticks. Default value is 50.
**/
Ticker.setInterval = function(interval) {
if (Ticker._intervalID != null) { clearInterval(Ticker._intervalID); }
Ticker._lastTime = Ticker._getTime();
Ticker._interval = interval;
Ticker._intervalID = setInterval(Ticker._tick, interval);
}
/**
* Returns the current target time between ticks, as set with setInterval.
* @method getInterval
* @static
* @return {Number} The current target interval in milliseconds between tick events.
**/
Ticker.getInterval = function() {
return Ticker._interval;
}
/**
* Returns the target frame rate in frames per second (FPS). For example, with an
* interval of 40, getFPS() will return 25 (1000ms per second divided by 40 ms per tick = 25fps).
* @method getFPS
* @static
* @return {Number} The current target number of frames / ticks broadcast per second.
**/
Ticker.getFPS = function() {
return 1000/Ticker._interval;
}
/**
* Sets the target frame rate in frames per second (FPS). For example, with an interval of 40, getFPS() will
* return 25 (1000ms per second divided by 40 ms per tick = 25fps).
* @method setFPS
* @static
* @param {Number} value Target number of ticks broadcast per second.
**/
Ticker.setFPS = function(value) {
Ticker.setInterval(1000/value);
}
/**
* Returns the actual frames / ticks per second.
* @method getMeasuredFPS
* @static
* @param {Number} ticks Optional. The number of previous ticks over which to measure the actual
* frames / ticks per second.
* @return {Number} The actual frames / ticks per second. Depending on performance, this may differ
* from the target frames per second.
**/
Ticker.getMeasuredFPS = function(ticks) {
if (Ticker._times.length < 2) { return -1; }
// x >> 1 : use bitwise to divide by two (int math)
if (ticks == null) { ticks = Ticker.getFPS()>>1; }
ticks = Math.min(Ticker._times.length-1, ticks);
return 1000/((Ticker._times[0]-Ticker._times[ticks])/ticks);
}
/**
* While Ticker is paused, pausable listeners are not ticked. See addListener for more information.
* @method setPaused
* @static
* @param {Boolean} value Indicates whether to pause (true) or unpause (false) Ticker.
**/
Ticker.setPaused = function(value) {
Ticker._paused = value;
}
/**
* Returns a boolean indicating whether Ticker is currently paused, as set with setPaused.
* @method getPaused
* @static
* @return {Boolean} Whether the Ticker is currently paused.
**/
Ticker.getPaused = function() {
return Ticker._paused;
}
/**
* Returns the number of milliseconds that have elapsed since the first tick event listener was added to
* Ticker. For example, you could use this in a time synchronized animation to determine the exact amount of
* time that has elapsed.
* @method getTime
* @static
* @param {Boolean} pauseable Indicates whether to include time elapsed
* while Ticker was paused. If false only time elapsed while Ticker is not paused will be returned.
* If true, the value returned will be total time elapsed since the first tick event listener was added.
* @return {Number} Number of milliseconds that have elapsed since Ticker was begun.
**/
Ticker.getTime = function(pauseable) {
return Ticker._getTime() - Ticker._startTime - (pauseable ? Ticker._pausedTime : 0);
}
/**
* Returns the number of ticks that have been broadcast by Ticker.
* @method getTicks
* @static
* @param {Boolean} pauseable Indicates whether to include ticks that would have been broadcast
* while Ticker was paused. If false only tick events broadcast while Ticker is not paused will be returned.
* If true, tick events that would have been broadcast while Ticker was paused will be included in the return
* value. The default value is false.
* @return {Number} of ticks that have been broadcast.
**/
Ticker.getTicks = function(pauseable) {
return Ticker._ticks - (pauseable ?Ticker._pausedTickers : 0);
}
// private static methods:
/**
* @method _tick
* @protected
**/
Ticker._tick = function() {
Ticker._ticks++;
var time = Ticker.getTime(false);
var elapsedTime = time-Ticker._lastTime;
var paused = Ticker._paused;
if (paused) {
Ticker._pausedTickers++;
Ticker._pausedTime += elapsedTime;
}
Ticker._lastTime = time;
var pauseable = Ticker._pauseable;
var listeners = Ticker._listeners;
var l = listeners ? listeners.length : 0;
for (var i=0; i<l; i++) {
var p = pauseable[i];
var listener = listeners[i];
if (listener == null || (paused && p) || listener.tick == null) { continue; }
listener.tick(elapsedTime);
}
Ticker._times.unshift(time);
if (Ticker._times.length > 100) { Ticker._times.pop(); }
}
/**
* @method _getTime
* @protected
**/
Ticker._getTime = function() {
return new Date().getTime();
}
//docced above
Ticker._startTime = Ticker._getTime();
window.Ticker = Ticker;
}(window));
/**
*
* @class Animation
*/
KISSY.add('fp-dsf/CatGameAnim', function(S, Event, util) {
var nullf = function() {}
var _reqAnimFrame = window.requestAnimationFrame;
var _cancelAnimFrame = window.cancelAnimationFrame;
(function() {
var a = ["ms", "moz", "webkit", "o"];
var i = a.length;
while (--i > -1 && !_reqAnimFrame) {
_reqAnimFrame = window[a[i] + "RequestAnimationFrame"];
_cancelAnimFrame = window[a[i] + "CancelAnimationFrame"] || window[a[i] + "CancelRequestAnimationFrame"];
}
})()
//--globals
var getTimeNow = function() {
return +new Date();
}
var _ticker = null;
var _tickerActive = false;
/**
* @class ticker
*/
var Ticker = util.Klass(null, {
__construct: function(fps, useRAF) {
var self = this;
//--attrs
util.mix(self, {
_startTime: getTimeNow(),
_id: null, //intervalId
_useRAF = useRAF !== false && _reqAnimFrame,
_fps: null,
//interval function use setTimeout if no _requestAnimFrame
_req: nullf,
_gap: 0,
_nextTime: 0,
_frame: 0,
time: 0
});
//--event
util.mix(self, S.EventTartget);
//--method
self._tick = function(manual) {
var id = self._id;
self.time = (getTimeNow() - self._startTime) / 1000;
var overlap = self.time - self._nextTime;
if (!self._fps || overlap > 0 || manual === true) {
self.frame++;
self._nextTime = overlap + (overlap >= self._gap ? 0.004 : self._gap - overlap);
self.fire('tick', {
time: self.time,
frames: self.frame
});
}
//fire('tick') may change self._id
if (manual !== true && id === self._id) {
self._id = self._req(self._tick);
}
}
self.fps(fps);
//ios6 bug reqanimFrame init error
setTimeout(function() {
if (self._useRAF && (!self._id || self.frame < 5)) self.useRAF(false);
}, 1500);
},
tick: function() {
var self = this;
self._tick(true);
},
sleep: function() {
var self = this;
if (self._id === null) return;
if (!self._useRAF || !_cancelAnimFrame) {
clearTimeout(self._id);
}
//make the req func be nullf
self._req = nullf;
self._id = null;
/////////////////wraning ---unknow _ticker///
if (self === _ticker) {
_tickerActive = false;
}
},
wake: function() {
var self = this;
var id = self._id;
if (id !== null) self.sleep();
function noRAF(f) {
var time = (self._nextTime - self.time) * 1000 + 1 | 0;
return setTimeout(util.proxy(self, f), time);
}
self._req = (self._fps === 0) ? nullf : (!_useRAF || !_reqAnimFrame) ? noRAF : _reqAnimFrame;
if (self === _ticker) _tickerActive = true;
self._tick(2);
},
fps: function(value) {
var self = this;
if (!arguments.length) return self._fps;
self._fps = value > 60 ? 60 : value;
self._gap = 1 / (_fps || 60);
self._nextTime = self.time + self._gap;
},
useRAF: function(value) {
var self = this;
if (!arguments.length) return self._useRAF;
self.sleep();
self._useRAF = value;
self.fps(self._fps);
}
});
_ticker = new Ticker(60, true);
}, {
requires: [
'dom', 'event', 'fp-dsf/CatGameUtil'
]
});
/*
* ----------------------------------------------------------------
* Ticker
* ----------------------------------------------------------------
*/
var _reqAnimFrame = window.requestAnimationFrame,
_cancelAnimFrame = window.cancelAnimationFrame,
_getTime = Date.now || function() {
return new Date().getTime();
};
//now try to determine the requestAnimationFrame and cancelAnimationFrame functions and if none are found, we'll use a setTimeout()/clearTimeout() polyfill.
a = ["ms", "moz", "webkit", "o"];
i = a.length;
while (--i > -1 && !_reqAnimFrame) {
_reqAnimFrame = window[a[i] + "RequestAnimationFrame"];
_cancelAnimFrame = window[a[i] + "CancelAnimationFrame"] || window[a[i] + "CancelRequestAnimationFrame"];
}
_class("Ticker", function(fps, useRAF) {
var _self = this,
_startTime = _getTime(),
_useRAF = (useRAF !== false && _reqAnimFrame),
_fps, _req, _id, _gap, _nextTime,
_tick = function(manual) {
_self.time = (_getTime() - _startTime) / 1000;
var id = _id,
overlap = _self.time - _nextTime;
if (!_fps || overlap > 0 || manual === true) {
_self.frame++;
_nextTime += overlap + (overlap >= _gap ? 0.004 : _gap - overlap);
_self.dispatchEvent("tick");
}
if (manual !== true && id === _id) {
//make sure the ids match in case the "tick" dispatch triggered something that caused the ticker to shut down or change _useRAF or something like that.
_id = _req(_tick);
}
};
EventDispatcher.call(_self);
this.time = this.frame = 0;
this.tick = function() {
_tick(true);
};
this.sleep = function() {
if (_id == null) {
return;
}
if (!_useRAF || !_cancelAnimFrame) {
clearTimeout(_id);
} else {
_cancelAnimFrame(_id);
}
_req = _emptyFunc;
_id = null;
if (_self === _ticker) {
_tickerActive = false;
}
};
this.wake = function() {
if (_id !== null) {
_self.sleep();
}
_req = (_fps === 0) ? _emptyFunc : (!_useRAF || !_reqAnimFrame) ? function(f) {
return setTimeout(f, ((_nextTime - _self.time) * 1000 + 1) | 0);
} : _reqAnimFrame;
if (_self === _ticker) {
_tickerActive = true;
}
_tick(2);
};
this.fps = function(value) {
if (!arguments.length) {
return _fps;
}
_fps = value;
_gap = 1 / (_fps || 60);
_nextTime = this.time + _gap;
_self.wake();
};
this.useRAF = function(value) {
if (!arguments.length) {
return _useRAF;
}
_self.sleep();
_useRAF = value;
_self.fps(_fps);
};
_self.fps(fps);
//a bug in iOS 6 Safari occasionally prevents the requestAnimationFrame from working initially, so we use a 1.5-second timeout that automatically falls back to setTimeout() if it senses this condition.
setTimeout(function() {
if (_useRAF && (!_id || _self.frame < 5)) {
_self.useRAF(false);
}
}, 1500);
});
p = gs.Ticker.prototype = new gs.events.EventDispatcher();
p.constructor = gs.Ticker;
/**
*
* @class Animation
*/
KISSY.add('fp-dsf/CatGameTicker', function(S, Event, util) {
var nullf = function() {}
var _reqAnimFrame = window.requestAnimationFrame;
var _cancelAnimFrame = window.cancelAnimationFrame;
(function() {
var a = ["ms", "moz", "webkit", "o"];
var i = a.length;
while (--i > -1 && !_reqAnimFrame) {
_reqAnimFrame = window[a[i] + "RequestAnimationFrame"];
_cancelAnimFrame = window[a[i] + "CancelAnimationFrame"] || window[a[i] + "CancelRequestAnimationFrame"];
}
})()
//--globals
var getTimeNow = function() {
return +new Date();
}
/**
* @Object ticker
* --use Ticker manage whole game
* --what other need to do is to regist on('tick') event
*/
var Ticker = util.mix({}, S.EventTarget);
util.mix(Ticker, {
init: function(fps, useRAF) {
var self = this;
//--attrs
util.mix(self, {
_startTime: getTimeNow(),
_id: null, //intervalId
_useRAF: useRAF !== false && _reqAnimFrame,
_fps: null,
//interval function use setTimeout if no _requestAnimFrame
_req: nullf,
_gap: 0,
_nextTime: 0,
_frame: 0,
time: 0,
_tickerActive: false
});
//--method
var _tick = function(manual) {
self.time = (getTimeNow() - self._startTime) / 1000;
var id = self._id;
var overlap = self.time - self._nextTime;
var deltaTime = 0;
// console.log("time-info", self.time, self._nextTime, overlap);
if (!self._fps || overlap > 0 || manual === true) {
self._frame += 1;
deltaTime = overlap + (overlap >= self._gap ? 0.004 : self._gap - overlap);
self._nextTime += deltaTime;
self.fire('tick', {
time: deltaTime > 0.1 ? self._gap : deltaTime,
frame: self._frame,
nextTime: self._nextTime
});
}
//fire('tick') may change self._id
if (manual !== true && id === self._id) {
// if(self._frame < 60) {
self._id = self._req.call(window, self._tick);
// console.log(self._id);
// }
// console.log("time :" + self.time)
}
}
self._tick = _tick;
self.fps(fps);
// ios6 bug reqanimFrame init error
setTimeout(function() {
if (self._useRAF && (!self._id || self.frame < 5)) self.useRAF(false);
}, 1500);
},
tick: function() {
var self = this;
self._tick(true);
},
sleep: function() {
var self = this;
if (self._id === null) return;
//clear _req
if (!self._useRAF || !_cancelAnimFrame) {
clearTimeout(self._id);
} else {
_cancelAnimFrame(self._id);
}
//make the req func be nullf
self._req = null;
self._id = null;
self.fire('sleep');
self._tickerActive = false;
},
wake: function() {
var self = this;
var id = self._id;
if (id !== null) self.sleep();
function noRAF(f) {
var time = (self._nextTime - self.time) * 1000 + 1 | 0;
return setTimeout(util.proxy(self, f), time);
}
self._req = (self._fps === 0) ? nullf : (!self._useRAF || !_reqAnimFrame) ? noRAF : _reqAnimFrame;
self._tickerActive = true;
self.fire('wakeup');
self._tick(2);
},
fps: function(value) {
var self = this;
if (!arguments.length) return self._fps;
self._fps = value > 60 ? 60 : value;
self._gap = 1 / (self._fps || 60);
self._nextTime = self.time + self._gap;
},
useRAF: function(value) {
var self = this;
if (!arguments.length) return self._useRAF;
self.sleep();
self._useRAF = value;
self.fps(self._fps);
}
});
Ticker.init(60, true);
window.ticker = Ticker;
return Ticker;
}, {
requires: [
'event', 'fp-dsf/CatGameUtil'
]
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment