1440 lines
31 KiB
JavaScript
1440 lines
31 KiB
JavaScript
|
/*
|
||
|
Playground 1.0.2
|
||
|
http://canvasquery.org
|
||
|
(c) 2012-2014 http://rezoner.net
|
||
|
Playground may be freely distributed under the MIT license.
|
||
|
*/
|
||
|
|
||
|
function playground(args) {
|
||
|
return new Playground(args);
|
||
|
};
|
||
|
|
||
|
/* utitlities */
|
||
|
|
||
|
playground.extend = function() {
|
||
|
for (var i = 1; i < arguments.length; i++) {
|
||
|
for (var j in arguments[i]) {
|
||
|
arguments[0][j] = arguments[i][j];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return arguments[0];
|
||
|
};
|
||
|
|
||
|
playground.throttle = function(fn, threshold) {
|
||
|
threshold || (threshold = 250);
|
||
|
var last,
|
||
|
deferTimer;
|
||
|
return function() {
|
||
|
var context = this;
|
||
|
|
||
|
var now = +new Date,
|
||
|
args = arguments;
|
||
|
if (last && now < last + threshold) {
|
||
|
// hold on to it
|
||
|
clearTimeout(deferTimer);
|
||
|
deferTimer = setTimeout(function() {
|
||
|
last = now;
|
||
|
fn.apply(context, args);
|
||
|
}, threshold);
|
||
|
} else {
|
||
|
last = now;
|
||
|
fn.apply(context, args);
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
/* constructor */
|
||
|
|
||
|
function Playground(args) {
|
||
|
|
||
|
/* defaults */
|
||
|
|
||
|
playground.extend(this, {
|
||
|
smoothing: 1,
|
||
|
scale: 1,
|
||
|
preventKeyboardDefault: true,
|
||
|
preventContextMenu: true
|
||
|
}, args);
|
||
|
|
||
|
|
||
|
if (!this.width || !this.height) this.fitToContainer = true;
|
||
|
|
||
|
if (!this.container) this.container = document.body;
|
||
|
if (this.container !== document.body) this.customContainer = true;
|
||
|
if (typeof this.container === "string") this.container = document.querySelector(this.container);
|
||
|
|
||
|
/* state */
|
||
|
|
||
|
this.state = {};
|
||
|
|
||
|
/* layer */
|
||
|
|
||
|
if (!args.layer) {
|
||
|
cq.smoothing = this.smoothing;
|
||
|
|
||
|
if (window.CocoonJS) {
|
||
|
this.layer = cq.cocoon(1, 1);
|
||
|
this.layer.appendTo(this.container);
|
||
|
this.screen = this.layer;
|
||
|
} else {
|
||
|
this.layer = cq(1, 1);
|
||
|
|
||
|
if (this.scaleToFit) {
|
||
|
this.screen = cq(1, 1);
|
||
|
this.screen.appendTo(this.container);
|
||
|
} else {
|
||
|
this.layer.appendTo(this.container);
|
||
|
this.screen = this.layer;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
var canvas = this.screen.canvas;
|
||
|
|
||
|
/* events */
|
||
|
|
||
|
this.eventsHandler = this.eventsHandler.bind(this);
|
||
|
|
||
|
/* mouse */
|
||
|
|
||
|
this.mouse = new playground.Mouse(canvas);
|
||
|
this.mouse.on("event", this.eventsHandler);
|
||
|
|
||
|
this.mouse.preventContextMenu = this.preventContextMenu;
|
||
|
|
||
|
/* touch */
|
||
|
|
||
|
this.touch = new playground.Touch(canvas);
|
||
|
this.touch.on("event", this.eventsHandler);
|
||
|
|
||
|
/* keyboard */
|
||
|
|
||
|
this.keyboard = new playground.Keyboard();
|
||
|
|
||
|
this.keyboard.preventDefault = this.preventKeyboardDefault;
|
||
|
this.keyboard.on("event", this.eventsHandler);
|
||
|
|
||
|
/* gamepads */
|
||
|
|
||
|
this.gamepads = new playground.Gamepads();
|
||
|
this.gamepads.on("event", this.eventsHandler);
|
||
|
|
||
|
/* window resize */
|
||
|
|
||
|
window.addEventListener("resize", this.resizeHandler.bind(this));
|
||
|
|
||
|
setTimeout(this.resizeHandler.bind(this), 1);
|
||
|
|
||
|
/* video recorder */
|
||
|
|
||
|
this.videoRecorder = new playground.VideoRecorder(this);
|
||
|
|
||
|
/* game loop */
|
||
|
|
||
|
var self = this;
|
||
|
|
||
|
var lastTick = Date.now();
|
||
|
|
||
|
function step() {
|
||
|
|
||
|
requestAnimationFrame(step);
|
||
|
|
||
|
var delta = Date.now() - lastTick;
|
||
|
lastTick = Date.now();
|
||
|
|
||
|
if (delta > 1000) return;
|
||
|
|
||
|
var dt = delta / 1000;
|
||
|
|
||
|
if (self.loader.count <= 0) {
|
||
|
|
||
|
if (self.step) self.step(dt);
|
||
|
if (self.state.step) self.state.step(dt);
|
||
|
|
||
|
if (self.render) self.render(dt);
|
||
|
if (self.state.render) self.state.render(dt);
|
||
|
|
||
|
if (self.postrender) self.postrender(dt);
|
||
|
if (self.state.postrender) self.state.postrender(dt);
|
||
|
|
||
|
} else {
|
||
|
self.renderLoader(dt);
|
||
|
}
|
||
|
|
||
|
if (self.scaleToFit) {
|
||
|
self.screen.save();
|
||
|
self.screen.translate(self.offsetX, self.offsetY);
|
||
|
self.screen.scale(self.scale, self.scale);
|
||
|
// self.layer.drawImage(self.scanlines.canvas, 0, 0);
|
||
|
self.screen.drawImage(self.layer.canvas, 0, 0);
|
||
|
self.screen.restore();
|
||
|
}
|
||
|
|
||
|
self.gamepads.step(dt);
|
||
|
self.videoRecorder.step(dt);
|
||
|
|
||
|
};
|
||
|
|
||
|
requestAnimationFrame(step);
|
||
|
|
||
|
/* assets */
|
||
|
|
||
|
/* default audio format */
|
||
|
|
||
|
var canPlayMp3 = (new Audio).canPlayType("audio/mp3");
|
||
|
var canPlayOgg = (new Audio).canPlayType('audio/ogg; codecs="vorbis"');
|
||
|
|
||
|
if (canPlayMp3) this.audioFormat = "mp3";
|
||
|
else this.audioFormat = "ogg";
|
||
|
|
||
|
this.loader = new playground.Loader();
|
||
|
|
||
|
this.images = {};
|
||
|
this.sounds = {};
|
||
|
|
||
|
this.loadFoo(0.5);
|
||
|
|
||
|
if (this.create) setTimeout(this.create.bind(this));
|
||
|
|
||
|
this.loader.on("ready", function() {
|
||
|
if (self.ready) self.ready();
|
||
|
|
||
|
self.ready = function() {};
|
||
|
});
|
||
|
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
Playground.prototype = {
|
||
|
|
||
|
setState: function(state) {
|
||
|
state.app = this;
|
||
|
|
||
|
if (this.state && this.state.leave) this.state.leave();
|
||
|
|
||
|
this.state = state;
|
||
|
|
||
|
if (this.state && this.state.enter) this.state.enter();
|
||
|
},
|
||
|
|
||
|
eventsHandler: function(event, data) {
|
||
|
|
||
|
if (this[event]) this[event](data);
|
||
|
if (this.state[event]) this.state[event](data);
|
||
|
|
||
|
},
|
||
|
|
||
|
resizeHandler: function() {
|
||
|
|
||
|
if (this.customContainer) {
|
||
|
var containerWidth = this.container.offsetWidth;
|
||
|
var containerHeight = this.container.offsetHeight;
|
||
|
} else {
|
||
|
var containerWidth = window.innerWidth;
|
||
|
var containerHeight = window.innerHeight;
|
||
|
}
|
||
|
|
||
|
if (this.fitToContainer) {
|
||
|
this.width = this.containerWidth;
|
||
|
this.height = this.containerHeight;
|
||
|
}
|
||
|
|
||
|
if (!this.scaleToFit) {
|
||
|
|
||
|
if (this.fitToContainer) {
|
||
|
this.width = containerWidth;
|
||
|
this.height = containerHeight;
|
||
|
}
|
||
|
|
||
|
this.offsetX = 0;
|
||
|
this.offsetY = 0;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
this.screen.width = containerWidth;
|
||
|
this.screen.height = containerHeight;
|
||
|
|
||
|
this.scale = Math.min(containerWidth / this.width, containerHeight / this.height);
|
||
|
|
||
|
if (this.roundScale) this.scale = Math.max(1, Math.floor(this.scale));
|
||
|
|
||
|
this.offsetX = containerWidth / 2 - this.scale * (this.width / 2) | 0;
|
||
|
this.offsetY = containerHeight / 2 - this.scale * (this.height / 2) | 0;
|
||
|
|
||
|
this.mouse.scale = this.scale;
|
||
|
this.mouse.offsetX = this.offsetX;
|
||
|
this.mouse.offsetY = this.offsetY;
|
||
|
|
||
|
this.touch.scale = this.scale;
|
||
|
this.touch.offsetX = this.offsetX;
|
||
|
this.touch.offsetY = this.offsetY;
|
||
|
}
|
||
|
|
||
|
this.layer.width = this.width;
|
||
|
this.layer.height = this.height;
|
||
|
|
||
|
this.center = {
|
||
|
x: this.width / 2 | 0,
|
||
|
y: this.height / 2 | 0
|
||
|
};
|
||
|
|
||
|
this.screen.clear("#000");
|
||
|
|
||
|
this.eventsHandler("resize");
|
||
|
|
||
|
this.mouse.update();
|
||
|
this.touch.update();
|
||
|
},
|
||
|
|
||
|
renderLoader: function() {
|
||
|
|
||
|
var height = this.height / 10 | 0;
|
||
|
var x = 32;
|
||
|
var width = this.width - x * 2;
|
||
|
var y = this.height / 2 - height / 2 | 0;
|
||
|
|
||
|
this.layer.clear("#000");
|
||
|
this.layer.strokeStyle("#fff").lineWidth(2).strokeRect(x, y, width, height);
|
||
|
this.layer.fillStyle("#fff").fillRect(x, y, width * this.loader.progress | 0, height);
|
||
|
|
||
|
},
|
||
|
|
||
|
record: function(args) {
|
||
|
this.videoRecorder.toggle(args);
|
||
|
},
|
||
|
|
||
|
/* imaginary timeout to delay loading */
|
||
|
|
||
|
loadFoo: function(timeout) {
|
||
|
if (!this.foofooLoader) this.foofooLoader = 0;
|
||
|
|
||
|
var loader = this.loader;
|
||
|
|
||
|
this.loader.add("foo " + timeout);
|
||
|
|
||
|
setTimeout(function() {
|
||
|
loader.ready("foo " + timeout);
|
||
|
}, (this.foofooLoader += timeout * 1000));
|
||
|
|
||
|
},
|
||
|
|
||
|
/* images */
|
||
|
|
||
|
loadImages: function() {
|
||
|
|
||
|
for (var i = 0; i < arguments.length; i++) {
|
||
|
|
||
|
var arg = arguments[i];
|
||
|
|
||
|
/* polymorphism at its finest */
|
||
|
|
||
|
if (typeof arg === "object") {
|
||
|
|
||
|
for (var key in arg) this.addImages(arg[key]);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
/* if argument is not an object/array let's try to load it */
|
||
|
|
||
|
var filename = arg;
|
||
|
|
||
|
var loader = this.loader;
|
||
|
|
||
|
var fileinfo = filename.match(/(.*)\..*/);
|
||
|
var key = fileinfo ? fileinfo[1] : filename;
|
||
|
|
||
|
/* filename defaults to png */
|
||
|
|
||
|
if (!fileinfo) filename += ".png";
|
||
|
|
||
|
var path = "images/" + filename;
|
||
|
|
||
|
this.loader.add(path);
|
||
|
|
||
|
var image = this.images[key] = new Image;
|
||
|
|
||
|
image.addEventListener("load", function() {
|
||
|
loader.ready(path);
|
||
|
});
|
||
|
|
||
|
image.addEventListener("error", function() {
|
||
|
loader.error(path);
|
||
|
});
|
||
|
|
||
|
image.src = path;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/* sounds */
|
||
|
|
||
|
loadSounds: function() {
|
||
|
|
||
|
for (var i = 0; i < arguments.length; i++) {
|
||
|
|
||
|
var arg = arguments[i];
|
||
|
|
||
|
/* polymorphism at its finest */
|
||
|
|
||
|
if (typeof arg === "object") {
|
||
|
|
||
|
for (var key in arg) this.loadSounds(arg[key]);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
/* if argument is not an object/array let's try to load it */
|
||
|
|
||
|
var filename = arg;
|
||
|
|
||
|
var loader = this.loader;
|
||
|
|
||
|
var key = filename;
|
||
|
|
||
|
filename += "." + this.audioFormat;
|
||
|
|
||
|
var path = "sounds/" + filename;
|
||
|
|
||
|
this.loader.add(path);
|
||
|
|
||
|
var audio = this.sounds[key] = new Audio;
|
||
|
|
||
|
audio.addEventListener("canplay", function() {
|
||
|
loader.ready(path);
|
||
|
});
|
||
|
|
||
|
audio.addEventListener("error", function() {
|
||
|
loader.error(path);
|
||
|
});
|
||
|
|
||
|
audio.src = path;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
loadFont: function(name) {
|
||
|
var styleNode = document.createElement("style");
|
||
|
styleNode.type = "text/css";
|
||
|
|
||
|
var formats = {
|
||
|
"woff": "woff",
|
||
|
"ttf": "truetype"
|
||
|
};
|
||
|
|
||
|
var sources = "";
|
||
|
|
||
|
for (var ext in formats) {
|
||
|
var type = formats[ext];
|
||
|
sources += " url(\"fonts/" + name + "." + ext + "\") format('" + type + "');"
|
||
|
}
|
||
|
|
||
|
styleNode.textContent = "@font-face { font-family: '" + name + "'; src: " + sources + " }";
|
||
|
|
||
|
document.head.appendChild(styleNode);
|
||
|
|
||
|
var layer = cq(32, 32);
|
||
|
|
||
|
layer.font("10px huj");
|
||
|
layer.fillText(16, 16, 16).trim();
|
||
|
|
||
|
var width = layer.width;
|
||
|
var height = layer.height;
|
||
|
|
||
|
this.loader.add("font " + name);
|
||
|
|
||
|
var self = this;
|
||
|
|
||
|
function check() {
|
||
|
var layer = cq(32, 32);
|
||
|
layer.font("10px " + name).fillText(16, 16, 16);
|
||
|
layer.trim();
|
||
|
|
||
|
if (layer.width !== width || layer.height !== height) {
|
||
|
self.loader.ready("font " + name);
|
||
|
} else {
|
||
|
setTimeout(check, 250);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
check();
|
||
|
},
|
||
|
|
||
|
playSound: function(key, loop) {
|
||
|
var sound = this.sounds[key];
|
||
|
sound.currentTime = 0;
|
||
|
sound.loop = loop;
|
||
|
sound.play();
|
||
|
return sound;
|
||
|
},
|
||
|
|
||
|
stopSound: function(sound) {
|
||
|
if (typeof sound === "string") sound = this.sounds[sound];
|
||
|
sound.pause();
|
||
|
}
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
playground.Events = function() {
|
||
|
|
||
|
this.listeners = {};
|
||
|
|
||
|
};
|
||
|
|
||
|
playground.Events.prototype = {
|
||
|
|
||
|
on: function(event, callback) {
|
||
|
if (!this.listeners[event]) this.listeners[event] = [];
|
||
|
|
||
|
this.listeners[event].push(callback);
|
||
|
|
||
|
return callback;
|
||
|
},
|
||
|
|
||
|
once: function(event, callback) {
|
||
|
callback.once = true;
|
||
|
|
||
|
if (!this.listeners[event]) this.listeners[event] = [];
|
||
|
|
||
|
this.listeners[event].push(callback);
|
||
|
|
||
|
return callback;
|
||
|
},
|
||
|
|
||
|
off: function(event, callback) {
|
||
|
for (var i = 0, len = this.listeners[event].length; i < len; i++) {
|
||
|
if (this.listeners[event][i]._remove) {
|
||
|
this.listeners[event].splice(i--, 1);
|
||
|
len--;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
trigger: function(event, data) {
|
||
|
|
||
|
/* if you prefer events pipe */
|
||
|
|
||
|
if (this.listeners["event"]) {
|
||
|
for (var i = 0, len = this.listeners["event"].length; i < len; i++) {
|
||
|
this.listeners["event"][i](event, data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* or subscribed to single event */
|
||
|
|
||
|
if (this.listeners[event]) {
|
||
|
for (var i = 0, len = this.listeners[event].length; i < len; i++) {
|
||
|
var listener = this.listeners[event][i];
|
||
|
listener(data);
|
||
|
|
||
|
if (listener.once) {
|
||
|
this.listeners[event].splice(i--, 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/* Mouse */
|
||
|
|
||
|
playground.Mouse = function(element) {
|
||
|
|
||
|
var self = this;
|
||
|
|
||
|
playground.Events.call(this);
|
||
|
|
||
|
this.element = element;
|
||
|
|
||
|
this.buttons = {};
|
||
|
|
||
|
this.mousemoveEvent = {};
|
||
|
this.mousedownEvent = {};
|
||
|
this.mouseupEvent = {};
|
||
|
this.mousewheelEvent = {};
|
||
|
|
||
|
this.x = 0;
|
||
|
this.y = 0;
|
||
|
|
||
|
this.offsetX = 0;
|
||
|
this.offsetY = 0;
|
||
|
this.scale = 1;
|
||
|
|
||
|
element.addEventListener("mousemove", this.mousemove.bind(this));
|
||
|
element.addEventListener("mousedown", this.mousedown.bind(this));
|
||
|
element.addEventListener("mouseup", this.mouseup.bind(this));
|
||
|
|
||
|
this.enableMousewheel();
|
||
|
|
||
|
this.element.addEventListener("contextmenu", function(e) {
|
||
|
if (self.preventContextMenu) e.preventDefault();
|
||
|
});
|
||
|
|
||
|
};
|
||
|
|
||
|
playground.Mouse.prototype = {
|
||
|
|
||
|
getElementOffset: function(element) {
|
||
|
|
||
|
var offsetX = 0;
|
||
|
var offsetY = 0;
|
||
|
|
||
|
do {
|
||
|
offsetX += element.offsetLeft;
|
||
|
offsetY += element.offsetTop;
|
||
|
}
|
||
|
|
||
|
while ((element = element.offsetParent));
|
||
|
|
||
|
return {
|
||
|
x: offsetX,
|
||
|
y: offsetY
|
||
|
};
|
||
|
|
||
|
},
|
||
|
|
||
|
update: function() {
|
||
|
this.elementOffset = this.getElementOffset(this.element);
|
||
|
},
|
||
|
|
||
|
mousemove: function(e) {
|
||
|
this.x = this.mousemoveEvent.x = (e.pageX - this.elementOffset.x - this.offsetX) / this.scale | 0;
|
||
|
this.y = this.mousemoveEvent.y = (e.pageY - this.elementOffset.y - this.offsetY) / this.scale | 0;
|
||
|
|
||
|
this.mousemoveEvent.original = e;
|
||
|
|
||
|
this.trigger("mousemove", this.mousemoveEvent);
|
||
|
},
|
||
|
|
||
|
mousedown: function(e) {
|
||
|
|
||
|
var buttonName = ["left", "middle", "right"][e.button];
|
||
|
|
||
|
this.mousedownEvent.x = this.mousemoveEvent.x;
|
||
|
this.mousedownEvent.y = this.mousemoveEvent.y;
|
||
|
this.mousedownEvent.button = buttonName;
|
||
|
this.mousedownEvent.original = e;
|
||
|
|
||
|
this[buttonName] = true;
|
||
|
|
||
|
this.trigger("mousedown", this.mousedownEvent);
|
||
|
},
|
||
|
|
||
|
mouseup: function(e) {
|
||
|
|
||
|
var buttonName = ["left", "middle", "right"][e.button];
|
||
|
|
||
|
this.mouseupEvent.x = this.mousemoveEvent.x;
|
||
|
this.mouseupEvent.y = this.mousemoveEvent.y;
|
||
|
this.mouseupEvent.button = buttonName;
|
||
|
this.mouseupEvent.original = e;
|
||
|
|
||
|
this[buttonName] = false;
|
||
|
|
||
|
this.trigger("mouseup", this.mouseupEvent);
|
||
|
},
|
||
|
|
||
|
mousewheel: function(e) {
|
||
|
this.mousewheelEvent.x = this.mousemoveEvent.x;
|
||
|
this.mousewheelEvent.y = this.mousemoveEvent.y;
|
||
|
this.mousewheelEvent.button = ["none", "left", "middle", "right"][e.button];
|
||
|
this.mousewheelEvent.original = e;
|
||
|
|
||
|
this[e.button] = false;
|
||
|
|
||
|
this.trigger("mousewheel", this.mousewheelEvent);
|
||
|
},
|
||
|
|
||
|
|
||
|
enableMousewheel: function() {
|
||
|
|
||
|
var eventNames = 'onwheel' in document || document.documentMode >= 9 ? ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'];
|
||
|
var callback = this.mousewheel.bind(this);
|
||
|
var self = this;
|
||
|
|
||
|
for (var i = eventNames.length; i;) {
|
||
|
|
||
|
self.element.addEventListener(eventNames[--i], playground.throttle(function(event) {
|
||
|
|
||
|
var orgEvent = event || window.event,
|
||
|
args = [].slice.call(arguments, 1),
|
||
|
delta = 0,
|
||
|
deltaX = 0,
|
||
|
deltaY = 0,
|
||
|
absDelta = 0,
|
||
|
absDeltaXY = 0,
|
||
|
fn;
|
||
|
event.type = "mousewheel";
|
||
|
|
||
|
// Old school scrollwheel delta
|
||
|
if (orgEvent.wheelDelta) {
|
||
|
delta = orgEvent.wheelDelta;
|
||
|
}
|
||
|
|
||
|
if (orgEvent.detail) {
|
||
|
delta = orgEvent.detail * -1;
|
||
|
}
|
||
|
|
||
|
// New school wheel delta (wheel event)
|
||
|
if (orgEvent.deltaY) {
|
||
|
deltaY = orgEvent.deltaY * -1;
|
||
|
delta = deltaY;
|
||
|
}
|
||
|
|
||
|
// Webkit
|
||
|
if (orgEvent.wheelDeltaY !== undefined) {
|
||
|
deltaY = orgEvent.wheelDeltaY;
|
||
|
}
|
||
|
|
||
|
var result = delta ? delta : deltaY;
|
||
|
|
||
|
self.mousewheelEvent.x = self.mousemoveEvent.x;
|
||
|
self.mousewheelEvent.y = self.mousemoveEvent.y;
|
||
|
self.mousewheelEvent.delta = result / Math.abs(result);
|
||
|
self.mousewheelEvent.original = orgEvent;
|
||
|
|
||
|
callback(self.mousewheelEvent);
|
||
|
|
||
|
event.preventDefault();
|
||
|
|
||
|
}, 40), false);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
playground.extend(playground.Mouse.prototype, playground.Events.prototype);
|
||
|
|
||
|
/* Touch */
|
||
|
|
||
|
playground.Touch = function(element) {
|
||
|
|
||
|
playground.Events.call(this);
|
||
|
|
||
|
this.element = element;
|
||
|
|
||
|
this.buttons = {};
|
||
|
|
||
|
this.touchmoveEvent = {};
|
||
|
this.touchstartEvent = {};
|
||
|
this.touchendEvent = {};
|
||
|
|
||
|
this.x = 0;
|
||
|
this.y = 0;
|
||
|
|
||
|
this.offsetX = 0;
|
||
|
this.offsetY = 0;
|
||
|
this.scale = 1;
|
||
|
|
||
|
element.addEventListener("touchmove", this.touchmove.bind(this));
|
||
|
element.addEventListener("touchstart", this.touchstart.bind(this));
|
||
|
element.addEventListener("touchend", this.touchend.bind(this));
|
||
|
};
|
||
|
|
||
|
playground.Touch.prototype = {
|
||
|
|
||
|
getElementOffset: function(element) {
|
||
|
|
||
|
var offsetX = 0;
|
||
|
var offsetY = 0;
|
||
|
|
||
|
do {
|
||
|
offsetX += element.offsetLeft;
|
||
|
offsetY += element.offsetTop;
|
||
|
}
|
||
|
|
||
|
while ((element = element.offsetParent));
|
||
|
|
||
|
return {
|
||
|
x: offsetX,
|
||
|
y: offsetY
|
||
|
};
|
||
|
|
||
|
},
|
||
|
|
||
|
update: function() {
|
||
|
this.elementOffset = this.getElementOffset(this.element);
|
||
|
},
|
||
|
|
||
|
touchmove: function(e) {
|
||
|
var touch = e.touches[0] || e.changedTouches[0];
|
||
|
|
||
|
this.x = this.touchmoveEvent.x = (touch.pageX - this.elementOffset.x - this.offsetX) / this.scale | 0;
|
||
|
this.y = this.touchmoveEvent.y = (touch.pageY - this.elementOffset.y - this.offsetY) / this.scale | 0;
|
||
|
|
||
|
this.touchmoveEvent.original = e;
|
||
|
this.touchmoveEvent.identifier = e.identifier;
|
||
|
|
||
|
this.trigger("touchmove", this.touchmoveEvent);
|
||
|
|
||
|
e.preventDefault();
|
||
|
},
|
||
|
|
||
|
touchstart: function(e) {
|
||
|
this.touchstartEvent.x = this.touchmoveEvent.x;
|
||
|
this.touchstartEvent.y = this.touchmoveEvent.y;
|
||
|
|
||
|
this.touchstartEvent.original = e;
|
||
|
this.touchstartEvent.identifier = e.identifier;
|
||
|
|
||
|
this.pressed = true;
|
||
|
|
||
|
this.trigger("touchstart", this.touchstartEvent);
|
||
|
},
|
||
|
|
||
|
touchend: function(e) {
|
||
|
this.touchendEvent.x = this.touchmoveEvent.x;
|
||
|
this.touchendEvent.y = this.touchmoveEvent.y;
|
||
|
|
||
|
this.touchendEvent.original = e;
|
||
|
this.touchendEvent.identifier = e.identifier;
|
||
|
|
||
|
this.pressed = false;
|
||
|
|
||
|
this.trigger("touchend", this.touchendEvent);
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
playground.extend(playground.Touch.prototype, playground.Events.prototype);
|
||
|
|
||
|
/* Keyboard */
|
||
|
|
||
|
playground.Keyboard = function() {
|
||
|
|
||
|
playground.Events.call(this);
|
||
|
|
||
|
this.keys = {};
|
||
|
|
||
|
document.addEventListener("keydown", this.keydown.bind(this));
|
||
|
document.addEventListener("keyup", this.keyup.bind(this));
|
||
|
document.addEventListener("keypress", this.keypress.bind(this));
|
||
|
|
||
|
this.keydownEvent = {};
|
||
|
this.keyupEvent = {};
|
||
|
|
||
|
};
|
||
|
|
||
|
playground.Keyboard.prototype = {
|
||
|
|
||
|
keycodes: {
|
||
|
37: "left",
|
||
|
38: "up",
|
||
|
39: "right",
|
||
|
40: "down",
|
||
|
45: "insert",
|
||
|
46: "delete",
|
||
|
8: "backspace",
|
||
|
9: "tab",
|
||
|
13: "enter",
|
||
|
16: "shift",
|
||
|
17: "ctrl",
|
||
|
18: "alt",
|
||
|
19: "pause",
|
||
|
20: "capslock",
|
||
|
27: "escape",
|
||
|
32: "space",
|
||
|
33: "pageup",
|
||
|
34: "pagedown",
|
||
|
35: "end",
|
||
|
36: "home",
|
||
|
112: "f1",
|
||
|
113: "f2",
|
||
|
114: "f3",
|
||
|
115: "f4",
|
||
|
116: "f5",
|
||
|
117: "f6",
|
||
|
118: "f7",
|
||
|
119: "f8",
|
||
|
120: "f9",
|
||
|
121: "f10",
|
||
|
122: "f11",
|
||
|
123: "f12",
|
||
|
144: "numlock",
|
||
|
145: "scrolllock",
|
||
|
186: "semicolon",
|
||
|
187: "equal",
|
||
|
188: "comma",
|
||
|
189: "dash",
|
||
|
190: "period",
|
||
|
191: "slash",
|
||
|
192: "graveaccent",
|
||
|
219: "openbracket",
|
||
|
220: "backslash",
|
||
|
221: "closebraket",
|
||
|
222: "singlequote"
|
||
|
},
|
||
|
|
||
|
keypress: function(e) {
|
||
|
|
||
|
},
|
||
|
|
||
|
keydown: function(e) {
|
||
|
if (e.which >= 48 && e.which <= 90) var keyName = String.fromCharCode(e.which).toLowerCase();
|
||
|
else var keyName = this.keycodes[e.which];
|
||
|
|
||
|
if (this.keys[keyName]) return;
|
||
|
|
||
|
this.keydownEvent.key = keyName;
|
||
|
this.keydownEvent.original = e;
|
||
|
|
||
|
this.keys[keyName] = true;
|
||
|
|
||
|
this.trigger("keydown", this.keydownEvent);
|
||
|
|
||
|
if (this.preventDefault && document.activeElement === document.body) {
|
||
|
e.returnValue = false;
|
||
|
e.keyCode = 0;
|
||
|
e.preventDefault();
|
||
|
e.stopPropagation();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
keyup: function(e) {
|
||
|
|
||
|
if (e.which >= 48 && e.which <= 90) var keyName = String.fromCharCode(e.which).toLowerCase();
|
||
|
else var keyName = this.keycodes[e.which];
|
||
|
|
||
|
this.keyupEvent.key = keyName;
|
||
|
this.keyupEvent.original = e;
|
||
|
|
||
|
this.keys[keyName] = false;
|
||
|
|
||
|
this.trigger("keyup", this.keyupEvent);
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
playground.extend(playground.Keyboard.prototype, playground.Events.prototype);
|
||
|
|
||
|
/* Gamepad */
|
||
|
|
||
|
playground.Gamepads = function() {
|
||
|
|
||
|
playground.Events.call(this);
|
||
|
|
||
|
this.getGamepads = navigator.getGamepads || navigator.webkitGetGamepads;
|
||
|
|
||
|
this.gamepadmoveEvent = {};
|
||
|
this.gamepaddownEvent = {};
|
||
|
this.gamepadupEvent = {};
|
||
|
|
||
|
this.gamepads = {};
|
||
|
|
||
|
};
|
||
|
|
||
|
playground.Gamepads.prototype = {
|
||
|
|
||
|
buttons: {
|
||
|
0: "1",
|
||
|
1: "2",
|
||
|
2: "3",
|
||
|
3: "4",
|
||
|
4: "l1",
|
||
|
5: "r1",
|
||
|
6: "l2",
|
||
|
7: "r2",
|
||
|
8: "select",
|
||
|
9: "start",
|
||
|
12: "up",
|
||
|
13: "down",
|
||
|
14: "left",
|
||
|
15: "right"
|
||
|
},
|
||
|
|
||
|
zeroState: function() {
|
||
|
var buttons = [];
|
||
|
|
||
|
for (var i = 0; i <= 15; i++) {
|
||
|
buttons.push({
|
||
|
pressed: false,
|
||
|
value: 0
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
axes: [],
|
||
|
buttons: buttons
|
||
|
};
|
||
|
},
|
||
|
|
||
|
createGamepad: function() {
|
||
|
var result = {
|
||
|
buttons: {},
|
||
|
sticks: [{
|
||
|
x: 0,
|
||
|
y: 0
|
||
|
}, {
|
||
|
x: 0,
|
||
|
y: 0
|
||
|
}]
|
||
|
};
|
||
|
|
||
|
|
||
|
for (var i = 0; i < 16; i++) {
|
||
|
var key = this.buttons[i];
|
||
|
result.buttons[key] = false;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
step: function() {
|
||
|
if (!navigator.getGamepads) return;
|
||
|
|
||
|
var gamepads = navigator.getGamepads();
|
||
|
|
||
|
for (var i = 0; i < gamepads.length; i++) {
|
||
|
var current = gamepads[i];
|
||
|
|
||
|
if (!current) continue;
|
||
|
|
||
|
if (!this[i]) this[i] = this.createGamepad();
|
||
|
|
||
|
/* have to concat the current.buttons because the are read-only */
|
||
|
|
||
|
var buttons = [].concat(current.buttons);
|
||
|
|
||
|
/* hack for missing dpads */
|
||
|
|
||
|
for (var h = 12; h <= 15; h++) {
|
||
|
if (!buttons[h]) buttons[h] = {
|
||
|
pressed: false,
|
||
|
value: 0
|
||
|
};
|
||
|
}
|
||
|
|
||
|
var previous = this[i];
|
||
|
|
||
|
/* axes (sticks) to buttons */
|
||
|
|
||
|
if (current.axes) {
|
||
|
|
||
|
if (current.axes[0] < 0) buttons[14].pressed = true;
|
||
|
if (current.axes[0] > 0) buttons[15].pressed = true;
|
||
|
if (current.axes[1] < 0) buttons[12].pressed = true;
|
||
|
if (current.axes[1] > 0) buttons[13].pressed = true;
|
||
|
|
||
|
previous.sticks[0].x = current.axes[0].value;
|
||
|
previous.sticks[0].y = current.axes[1].value;
|
||
|
previous.sticks[1].x = current.axes[2].value;
|
||
|
previous.sticks[1].y = current.axes[3].value;
|
||
|
|
||
|
}
|
||
|
|
||
|
/* check buttons changes */
|
||
|
|
||
|
for (var j = 0; j < buttons.length; j++) {
|
||
|
|
||
|
var key = this.buttons[j];
|
||
|
|
||
|
/* gamepad down */
|
||
|
|
||
|
if (buttons[j].pressed && !previous.buttons[key]) {
|
||
|
previous.buttons[key] = true;
|
||
|
this.gamepaddownEvent.button = this.buttons[j];
|
||
|
this.gamepaddownEvent.gamepad = i;
|
||
|
this.trigger("gamepaddown", this.gamepaddownEvent);
|
||
|
}
|
||
|
|
||
|
/* gamepad up */
|
||
|
else if (!buttons[j].pressed && previous.buttons[key]) {
|
||
|
previous.buttons[key] = false;
|
||
|
this.gamepadupEvent.button = this.buttons[j];
|
||
|
this.gamepadupEvent.gamepad = i;
|
||
|
this.trigger("gamepadup", this.gamepadupEvent);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
};
|
||
|
|
||
|
playground.extend(playground.Gamepads.prototype, playground.Events.prototype);
|
||
|
|
||
|
|
||
|
/* Loader */
|
||
|
|
||
|
playground.Loader = function() {
|
||
|
|
||
|
playground.Events.call(this);
|
||
|
|
||
|
this.reset();
|
||
|
|
||
|
};
|
||
|
|
||
|
playground.Loader.prototype = {
|
||
|
|
||
|
/* loader */
|
||
|
|
||
|
add: function(id) {
|
||
|
this.queue++;
|
||
|
this.count++;
|
||
|
this.trigger("add", id);
|
||
|
},
|
||
|
|
||
|
error: function(id) {
|
||
|
console.log("unable to load " + id);
|
||
|
this.trigger("error", id);
|
||
|
},
|
||
|
|
||
|
ready: function(id) {
|
||
|
this.queue--;
|
||
|
|
||
|
this.progress = 1 - this.queue / this.count;
|
||
|
|
||
|
this.trigger("load", id);
|
||
|
|
||
|
if (this.queue <= 0) {
|
||
|
this.trigger("ready");
|
||
|
this.reset();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
reset: function() {
|
||
|
this.progress = 0;
|
||
|
this.queue = 0;
|
||
|
this.count = 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
playground.extend(playground.Loader.prototype, playground.Events.prototype);
|
||
|
|
||
|
CanvasQuery.Layer.prototype.playground = function(args) {
|
||
|
args.layer = this;
|
||
|
return playground(args);
|
||
|
};
|
||
|
|
||
|
/* Video recorder */
|
||
|
|
||
|
/* whammy - https://github.com/antimatter15/whammy */
|
||
|
|
||
|
window.Whammy = function() {
|
||
|
function h(a, b) {
|
||
|
for (var c = r(a), c = [{
|
||
|
id: 440786851,
|
||
|
data: [{
|
||
|
data: 1,
|
||
|
id: 17030
|
||
|
}, {
|
||
|
data: 1,
|
||
|
id: 17143
|
||
|
}, {
|
||
|
data: 4,
|
||
|
id: 17138
|
||
|
}, {
|
||
|
data: 8,
|
||
|
id: 17139
|
||
|
}, {
|
||
|
data: "webm",
|
||
|
id: 17026
|
||
|
}, {
|
||
|
data: 2,
|
||
|
id: 17031
|
||
|
}, {
|
||
|
data: 2,
|
||
|
id: 17029
|
||
|
}]
|
||
|
}, {
|
||
|
id: 408125543,
|
||
|
data: [{
|
||
|
id: 357149030,
|
||
|
data: [{
|
||
|
data: 1E6,
|
||
|
id: 2807729
|
||
|
}, {
|
||
|
data: "whammy",
|
||
|
id: 19840
|
||
|
}, {
|
||
|
data: "whammy",
|
||
|
id: 22337
|
||
|
}, {
|
||
|
data: s(c.duration),
|
||
|
id: 17545
|
||
|
}]
|
||
|
}, {
|
||
|
id: 374648427,
|
||
|
data: [{
|
||
|
id: 174,
|
||
|
data: [{
|
||
|
data: 1,
|
||
|
id: 215
|
||
|
}, {
|
||
|
data: 1,
|
||
|
id: 25541
|
||
|
}, {
|
||
|
data: 0,
|
||
|
id: 156
|
||
|
}, {
|
||
|
data: "und",
|
||
|
id: 2274716
|
||
|
}, {
|
||
|
data: "V_VP8",
|
||
|
id: 134
|
||
|
}, {
|
||
|
data: "VP8",
|
||
|
id: 2459272
|
||
|
}, {
|
||
|
data: 1,
|
||
|
id: 131
|
||
|
}, {
|
||
|
id: 224,
|
||
|
data: [{
|
||
|
data: c.width,
|
||
|
id: 176
|
||
|
}, {
|
||
|
data: c.height,
|
||
|
id: 186
|
||
|
}]
|
||
|
}]
|
||
|
}]
|
||
|
}]
|
||
|
}], e = 0, d = 0; e < a.length;) {
|
||
|
var g = [],
|
||
|
f = 0;
|
||
|
do g.push(a[e]), f += a[e].duration, e++; while (e < a.length && 3E4 > f);
|
||
|
var h = 0,
|
||
|
g = {
|
||
|
id: 524531317,
|
||
|
data: [{
|
||
|
data: d,
|
||
|
id: 231
|
||
|
}].concat(g.map(function(a) {
|
||
|
var b = t({
|
||
|
discardable: 0,
|
||
|
frame: a.data.slice(4),
|
||
|
invisible: 0,
|
||
|
keyframe: 1,
|
||
|
lacing: 0,
|
||
|
trackNum: 1,
|
||
|
timecode: Math.round(h)
|
||
|
});
|
||
|
h += a.duration;
|
||
|
return {
|
||
|
data: b,
|
||
|
id: 163
|
||
|
}
|
||
|
}))
|
||
|
};
|
||
|
c[1].data.push(g);
|
||
|
d += f
|
||
|
}
|
||
|
return m(c, b)
|
||
|
}
|
||
|
|
||
|
function r(a) {
|
||
|
for (var b = a[0].width, c = a[0].height, e = a[0].duration,
|
||
|
d = 1; d < a.length; d++) {
|
||
|
if (a[d].width != b) throw "Frame " + (d + 1) + " has a different width";
|
||
|
if (a[d].height != c) throw "Frame " + (d + 1) + " has a different height";
|
||
|
if (0 > a[d].duration || 32767 < a[d].duration) throw "Frame " + (d + 1) + " has a weird duration (must be between 0 and 32767)";
|
||
|
e += a[d].duration
|
||
|
}
|
||
|
return {
|
||
|
duration: e,
|
||
|
width: b,
|
||
|
height: c
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function u(a) {
|
||
|
for (var b = []; 0 < a;) b.push(a & 255), a >>= 8;
|
||
|
return new Uint8Array(b.reverse())
|
||
|
}
|
||
|
|
||
|
function n(a) {
|
||
|
var b = [];
|
||
|
a = (a.length % 8 ? Array(9 - a.length % 8).join("0") : "") + a;
|
||
|
for (var c = 0; c < a.length; c += 8) b.push(parseInt(a.substr(c,
|
||
|
8), 2));
|
||
|
return new Uint8Array(b)
|
||
|
}
|
||
|
|
||
|
function m(a, b) {
|
||
|
for (var c = [], e = 0; e < a.length; e++) {
|
||
|
var d = a[e].data;
|
||
|
"object" == typeof d && (d = m(d, b));
|
||
|
"number" == typeof d && (d = n(d.toString(2)));
|
||
|
if ("string" == typeof d) {
|
||
|
for (var g = new Uint8Array(d.length), f = 0; f < d.length; f++) g[f] = d.charCodeAt(f);
|
||
|
d = g
|
||
|
}
|
||
|
f = d.size || d.byteLength || d.length;
|
||
|
g = Math.ceil(Math.ceil(Math.log(f) / Math.log(2)) / 8);
|
||
|
f = f.toString(2);
|
||
|
f = Array(7 * g + 8 - f.length).join("0") + f;
|
||
|
g = Array(g).join("0") + "1" + f;
|
||
|
c.push(u(a[e].id));
|
||
|
c.push(n(g));
|
||
|
c.push(d)
|
||
|
}
|
||
|
return b ? (c = p(c), new Uint8Array(c)) :
|
||
|
new Blob(c, {
|
||
|
type: "video/webm"
|
||
|
})
|
||
|
}
|
||
|
|
||
|
function p(a, b) {
|
||
|
null == b && (b = []);
|
||
|
for (var c = 0; c < a.length; c++) "object" == typeof a[c] ? p(a[c], b) : b.push(a[c]);
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
function t(a) {
|
||
|
var b = 0;
|
||
|
a.keyframe && (b |= 128);
|
||
|
a.invisible && (b |= 8);
|
||
|
a.lacing && (b |= a.lacing << 1);
|
||
|
a.discardable && (b |= 1);
|
||
|
if (127 < a.trackNum) throw "TrackNumber > 127 not supported";
|
||
|
return [a.trackNum | 128, a.timecode >> 8, a.timecode & 255, b].map(function(a) {
|
||
|
return String.fromCharCode(a)
|
||
|
}).join("") + a.frame
|
||
|
}
|
||
|
|
||
|
function q(a) {
|
||
|
for (var b = a.RIFF[0].WEBP[0], c = b.indexOf("\u009d\u0001*"),
|
||
|
e = 0, d = []; 4 > e; e++) d[e] = b.charCodeAt(c + 3 + e);
|
||
|
e = d[1] << 8 | d[0];
|
||
|
c = e & 16383;
|
||
|
e = d[3] << 8 | d[2];
|
||
|
return {
|
||
|
width: c,
|
||
|
height: e & 16383,
|
||
|
data: b,
|
||
|
riff: a
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function k(a) {
|
||
|
for (var b = 0, c = {}; b < a.length;) {
|
||
|
var e = a.substr(b, 4),
|
||
|
d = parseInt(a.substr(b + 4, 4).split("").map(function(a) {
|
||
|
a = a.charCodeAt(0).toString(2);
|
||
|
return Array(8 - a.length + 1).join("0") + a
|
||
|
}).join(""), 2),
|
||
|
g = a.substr(b + 4 + 4, d),
|
||
|
b = b + (8 + d);
|
||
|
c[e] = c[e] || [];
|
||
|
"RIFF" == e || "LIST" == e ? c[e].push(k(g)) : c[e].push(g)
|
||
|
}
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
function s(a) {
|
||
|
return [].slice.call(new Uint8Array((new Float64Array([a])).buffer),
|
||
|
0).map(function(a) {
|
||
|
return String.fromCharCode(a)
|
||
|
}).reverse().join("")
|
||
|
}
|
||
|
|
||
|
function l(a, b) {
|
||
|
this.frames = [];
|
||
|
this.duration = 1E3 / a;
|
||
|
this.quality = b || .8
|
||
|
}
|
||
|
l.prototype.add = function(a, b) {
|
||
|
if ("undefined" != typeof b && this.duration) throw "you can't pass a duration if the fps is set";
|
||
|
if ("undefined" == typeof b && !this.duration) throw "if you don't have the fps set, you ned to have durations here.";
|
||
|
"canvas" in a && (a = a.canvas);
|
||
|
if ("toDataURL" in a) a = a.toDataURL("image/webp", this.quality);
|
||
|
else if ("string" != typeof a) throw "frame must be a a HTMLCanvasElement, a CanvasRenderingContext2D or a DataURI formatted string";
|
||
|
if (!/^data:image\/webp;base64,/ig.test(a)) throw "Input must be formatted properly as a base64 encoded DataURI of type image/webp";
|
||
|
this.frames.push({
|
||
|
image: a,
|
||
|
duration: b || this.duration
|
||
|
})
|
||
|
};
|
||
|
l.prototype.compile = function(a) {
|
||
|
return new h(this.frames.map(function(a) {
|
||
|
var c = q(k(atob(a.image.slice(23))));
|
||
|
c.duration = a.duration;
|
||
|
return c
|
||
|
}), a)
|
||
|
};
|
||
|
return {
|
||
|
Video: l,
|
||
|
fromImageArray: function(a, b, c) {
|
||
|
return h(a.map(function(a) {
|
||
|
a = q(k(atob(a.slice(23))));
|
||
|
a.duration = 1E3 / b;
|
||
|
return a
|
||
|
}), c)
|
||
|
},
|
||
|
toWebM: h
|
||
|
}
|
||
|
}();
|
||
|
|
||
|
playground.VideoRecorder = function(app, args) {
|
||
|
|
||
|
this.app = app;
|
||
|
|
||
|
};
|
||
|
|
||
|
playground.VideoRecorder.prototype = {
|
||
|
|
||
|
setup: function(args) {
|
||
|
|
||
|
this.region = false;
|
||
|
|
||
|
playground.extend(this, {
|
||
|
followMouse: false,
|
||
|
framerate: 20,
|
||
|
scale: 1
|
||
|
}, args);
|
||
|
|
||
|
if (!this.region) {
|
||
|
this.region = [0, 0, this.app.layer.width, this.app.layer.height];
|
||
|
}
|
||
|
|
||
|
this.playbackRate = this.framerate / 60;
|
||
|
|
||
|
this.layer = cq(this.region[2] * this.scale | 0, this.region[3] * this.scale | 0);
|
||
|
},
|
||
|
|
||
|
start: function(args) {
|
||
|
this.setup(args);
|
||
|
this.encoder = new Whammy.Video(this.framerate);
|
||
|
this.captureTimeout = 0;
|
||
|
this.recording = true;
|
||
|
},
|
||
|
|
||
|
step: function(delta) {
|
||
|
|
||
|
if (this.encoder) {
|
||
|
|
||
|
this.captureTimeout -= delta * 1000;
|
||
|
|
||
|
if (this.captureTimeout <= 0) {
|
||
|
this.captureTimeout = 1000 / this.framerate + this.captureTimeout;
|
||
|
|
||
|
this.layer.drawImage(this.app.layer.canvas, this.region[0], this.region[1], this.region[2], this.region[3], 0, 0, this.layer.width, this.layer.height);
|
||
|
this.encoder.add(this.layer.canvas);
|
||
|
}
|
||
|
|
||
|
this.app.screen.save().lineWidth(8).strokeStyle("#c00").strokeRect(0, 0, this.app.screen.width, this.app.screen.height).restore();
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
stop: function() {
|
||
|
if (!this.encoder) return;
|
||
|
var output = this.encoder.compile();
|
||
|
var url = (window.webkitURL || window.URL).createObjectURL(output);
|
||
|
window.open(url);
|
||
|
this.recording = false;
|
||
|
|
||
|
delete this.encoder;
|
||
|
},
|
||
|
|
||
|
toggle: function(args) {
|
||
|
if (this.encoder) this.stop();
|
||
|
else this.start(args);
|
||
|
}
|
||
|
|
||
|
};
|