liblooper
Version:
Looper implementation for use in conjonction with the HJS-MESSAGE messaging API. This is the browser version of the
480 lines (433 loc) • 15.7 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MessageLooper = exports.Looper = undefined;
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _events = require('events');
var _events2 = _interopRequireDefault(_events);
var _handler = require('hjs-message/lib/handler');
var _queue = require('hjs-message/lib/queue');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var simulationTimestep = 1000 / 60,
frameDelta = 0,
lastFrameTimeMs = 0,
fps = 60,
lastFpsUpdate = 0,
framesThisSecond = 0,
numUpdateSteps = 0,
minFrameDelay = 0,
running = false,
started = false,
panic = false; /** @babel */
var requestAnimationFrame = window.requestAnimationFrame || function () {
var lastTimestamp = Date.now(),
now = void 0,
timeout = void 0;
return function (callback) {
now = Date.now();
timeout = Math.max(0, simulationTimestep - (now - lastTimestamp));
lastTimestamp = now + timeout;
return setTimeout(function () {
callback(now + timeout);
}, timeout);
};
}();
var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
var NOOP = function NOOP() {};
var begin = NOOP,
update = NOOP,
draw = NOOP,
end = NOOP,
rafHandle = void 0;
var loop = function loop(timestamp) {
rafHandle = requestAnimationFrame(loop);
if (timestamp < lastFrameTimeMs + minFrameDelay) {
return;
}
frameDelta += timestamp - lastFrameTimeMs;
lastFrameTimeMs = timestamp;
begin(timestamp, frameDelta);
if (timestamp > lastFpsUpdate + 1000) {
fps = 0.25 * framesThisSecond + 0.75 * fps;
lastFpsUpdate = timestamp;
framesThisSecond = 0;
}
framesThisSecond++;
numUpdateSteps = 0;
while (frameDelta >= simulationTimestep) {
update(simulationTimestep);
frameDelta -= simulationTimestep;
if (++numUpdateSteps >= 240) {
panic = true;
break;
}
}
draw(frameDelta / simulationTimestep);
end(fps, panic);
panic = false;
};
var MainLoop = {
getSimulationTimestep: function getSimulationTimestep() {
return simulationTimestep;
},
setSimulationTimestep: function setSimulationTimestep(timestep) {
simulationTimestep = timestep;
return this;
},
getFPS: function getFPS() {
return fps;
},
getMaxAllowedFPS: function getMaxAllowedFPS() {
return 1000 / minFrameDelay;
},
setMaxAllowedFPS: function setMaxAllowedFPS() {
var fps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Infinity;
if (fps === 0) {
fps = 60;
}
minFrameDelay = 1000 / fps;
return this;
},
resetFrameDelta: function resetFrameDelta() {
var oldFrameDelta = frameDelta;
frameDelta = 0;
return oldFrameDelta;
},
setBegin: function setBegin(fun) {
begin = fun || begin;
return this;
},
setUpdate: function setUpdate(fun) {
update = fun || update;
return this;
},
setDraw: function setDraw(fun) {
draw = fun || draw;
return this;
},
setEnd: function setEnd(fun) {
end = fun || end;
return this;
},
start: function start() {
if (!started) {
started = true;
rafHandle = requestAnimationFrame(function (timestamp) {
draw(1);
running = true;
lastFrameTimeMs = timestamp;
lastFpsUpdate = timestamp;
framesThisSecond = 0;
rafHandle = requestAnimationFrame(loop);
});
}
return this;
},
stop: function stop() {
running = false;
started = false;
cancelAnimationFrame(rafHandle);
return this;
},
isRunning: function isRunning() {
return running;
}
};
var PAUSED = false;
var MAIN_LOOPER = null;
var LOOPER = null;
var prepareMainLooper = function prepareMainLooper() {
var quitAllowed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var fps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 60;
if (LOOPER) {
throw new ReferenceError("RuntimeException Only one Looper may be created per application");
}
return LOOPER = new Looper(quitAllowed, fps);
};
var EXIT_FRAME = 'exit_frame';
var RENDER_FRAME = 'render_frame';
var Looper = exports.Looper = function (_EventEmitter) {
(0, _inherits3.default)(Looper, _EventEmitter);
function Looper() {
var quitAllowed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var fps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 60;
(0, _classCallCheck3.default)(this, Looper);
var _this = (0, _possibleConstructorReturn3.default)(this, (Looper.__proto__ || (0, _getPrototypeOf2.default)(Looper)).call(this));
_this.mQueue = new _queue.MessageQueue(quitAllowed);
_this.mQueue.mLooper = _this;
MainLoop.setMaxAllowedFPS(fps).setUpdate(function (delta) {
_this.loop(delta);
}).setDraw(function (interpolation) {
_this.render(interpolation);
}).setEnd(function (fps, panic) {
_this.exit(fps, panic);
});
return _this;
}
(0, _createClass3.default)(Looper, [{
key: 'exit',
value: function exit(fps, panic) {
this.emit(EXIT_FRAME, { fps: fps, panic: panic });
}
}, {
key: 'getMainLooper',
value: function getMainLooper() {
return MainLoop;
}
}, {
key: 'getQueue',
value: function getQueue() {
return this.mQueue;
}
}, {
key: 'isIdling',
value: function isIdling() {
return this.mQueue.isIdle();
}
}, {
key: 'isRunning',
value: function isRunning() {
return MainLoop.isRunning();
}
}, {
key: 'loop',
value: function loop(delta) {
var queue = this.mQueue,
msg = queue.nextMessage();
if (msg) {
var target = msg.target;
if (!target) {
return;
}
var handled = target.dispatchMessage(msg);
if (!handled) {
target.unHandleMessage(msg);
}
msg.recycle();
}
}
}, {
key: 'pause',
value: function pause() {
if (!PAUSED) {
PAUSED = !PAUSED;
MainLoop.stop();
}
}
}, {
key: 'quit',
value: function quit() {
var safe = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
MainLoop.stop();
this.mQueue.quit(safe);
if (safe) {
this.removeAllListeners(EXIT_FRAME);
this.removeAllListeners(RENDER_FRAME);
this.mQueue = null;
}
}
}, {
key: 'quitSafely',
value: function quitSafely() {
this.quit(true);
}
}, {
key: 'render',
value: function render(interpolation) {
this.emit(RENDER_FRAME, { interpolation: interpolation });
}
}, {
key: 'resume',
value: function resume() {
if (PAUSED) {
PAUSED = !PAUSED;
MainLoop.start();
}
}
}, {
key: 'start',
value: function start() {
if (!this.isRunning()) {
return MainLoop.start();
}
}
}, {
key: 'toggle',
value: function toggle() {
PAUSED ? this.resume() : this.pause();
}
}], [{
key: 'createLoopHandler',
value: function createLoopHandler() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$callback = _ref.callback,
callback = _ref$callback === undefined ? null : _ref$callback,
_ref$asynchronous = _ref.asynchronous,
asynchronous = _ref$asynchronous === undefined ? true : _ref$asynchronous,
_ref$messenger = _ref.messenger,
messenger = _ref$messenger === undefined ? new _handler.Messenger() : _ref$messenger,
_ref$handleMessage = _ref.handleMessage,
handleMessage = _ref$handleMessage === undefined ? null : _ref$handleMessage,
_ref$unHandleMessage = _ref.unHandleMessage,
unHandleMessage = _ref$unHandleMessage === undefined ? null : _ref$unHandleMessage,
_ref$handleExit = _ref.handleExit,
handleExit = _ref$handleExit === undefined ? null : _ref$handleExit,
_ref$handleRender = _ref.handleRender,
handleRender = _ref$handleRender === undefined ? null : _ref$handleRender,
_ref$scheduleTime = _ref.scheduleTime,
scheduleTime = _ref$scheduleTime === undefined ? 200 : _ref$scheduleTime,
_ref$quitAllowed = _ref.quitAllowed,
quitAllowed = _ref$quitAllowed === undefined ? true : _ref$quitAllowed,
_ref$fps = _ref.fps,
fps = _ref$fps === undefined ? 60 : _ref$fps;
var looper = Looper.myLooper();
if (!looper) {
looper = Looper.prepare(quitAllowed, fps);
}
return new MessageLooper({
callback: callback,
asynchronous: asynchronous,
messenger: messenger,
handleMessage: handleMessage,
unHandleMessage: unHandleMessage,
handleExit: handleExit,
handleRender: handleRender,
scheduleTime: scheduleTime,
looper: looper
});
}
}, {
key: 'myLooper',
value: function myLooper() {
return LOOPER;
}
}, {
key: 'myQueue',
value: function myQueue() {
var looper = Looper.myLooper();
if (looper) {
return looper.mQueue;
}
return null;
}
}, {
key: 'prepare',
value: function prepare() {
var quitAllowed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var fps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 60;
var looper = prepareMainLooper(quitAllowed, fps);
if (MAIN_LOOPER) {
throw new ReferenceError("IllegalStateException The main Looper has already been prepared.");
}
MAIN_LOOPER = looper.start();
}
}]);
return Looper;
}(_events2.default);
var MessageLooper = exports.MessageLooper = function (_MessageHandler) {
(0, _inherits3.default)(MessageLooper, _MessageHandler);
function MessageLooper() {
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref2$callback = _ref2.callback,
callback = _ref2$callback === undefined ? null : _ref2$callback,
_ref2$asynchronous = _ref2.asynchronous,
asynchronous = _ref2$asynchronous === undefined ? true : _ref2$asynchronous,
_ref2$messenger = _ref2.messenger,
messenger = _ref2$messenger === undefined ? new _handler.Messenger() : _ref2$messenger,
_ref2$handleMessage = _ref2.handleMessage,
handleMessage = _ref2$handleMessage === undefined ? null : _ref2$handleMessage,
_ref2$unHandleMessage = _ref2.unHandleMessage,
unHandleMessage = _ref2$unHandleMessage === undefined ? null : _ref2$unHandleMessage,
_ref2$handleRender = _ref2.handleRender,
handleRender = _ref2$handleRender === undefined ? null : _ref2$handleRender,
_ref2$handleExit = _ref2.handleExit,
handleExit = _ref2$handleExit === undefined ? null : _ref2$handleExit,
_ref2$scheduleTime = _ref2.scheduleTime,
scheduleTime = _ref2$scheduleTime === undefined ? 200 : _ref2$scheduleTime,
_ref2$looper = _ref2.looper,
looper = _ref2$looper === undefined ? null : _ref2$looper;
(0, _classCallCheck3.default)(this, MessageLooper);
var _this2 = (0, _possibleConstructorReturn3.default)(this, (MessageLooper.__proto__ || (0, _getPrototypeOf2.default)(MessageLooper)).call(this, { callback: callback, asynchronous: asynchronous, queue: looper ? looper.getQueue() : Looper.myQueue(), messenger: messenger, handleMessage: handleMessage, unHandleMessage: unHandleMessage, scheduleTime: scheduleTime }));
_this2.looper = looper || Looper.myLooper();
if (handleRender) {
_this2.handleRender = handleRender;
}
if (handleExit) {
_this2.handleExit = handleExit;
}
if (_this2.looper) {
_this2.looper.on(RENDER_FRAME, function (_ref3) {
var interpolation = _ref3.interpolation;
_this2.handleRender(interpolation);
});
_this2.looper.on(EXIT_FRAME, function (_ref4) {
var fps = _ref4.fps,
panic = _ref4.panic;
_this2.handleRender(fps, panic);
});
}
return _this2;
}
(0, _createClass3.default)(MessageLooper, [{
key: 'exit',
value: function exit() {
if (this.looper) {
this.looper.quitSafely();
}
}
}, {
key: 'getLooper',
value: function getLooper() {
if (this.looper) {
return this.looper;
}
return null;
}
}, {
key: 'getMainLooper',
value: function getMainLooper() {
if (this.looper) {
return this.looper.getMainLooper();
}
return null;
}
}, {
key: 'handleExit',
value: function handleExit(fps, panic) {}
}, {
key: 'handleRender',
value: function handleRender(interpolation) {}
}, {
key: 'pause',
value: function pause() {
if (this.looper) {
this.looper.pause();
}
}
}, {
key: 'resume',
value: function resume() {
if (this.looper) {
this.looper.resume();
}
}
}, {
key: 'toggle',
value: function toggle() {
if (this.looper) {
this.looper.toggle();
}
}
}]);
return MessageLooper;
}(_handler.MessageHandler);