|
<!doctype html>
<html>
<head>
<title>.pixl</title>
<meta charset="utf-8" />
<style type="text/css">
body { overflow: hidden; }
canvas {
position: absolute;
top: 0;
left: 0;
}
#status {
position: absolute;
bottom: 0;
right: 0;
}
#pos, #online { display: inline-block; vertical-align: bottom; }
#online {
width: 20px;
height: 20px;
background-color: green;
}
</style>
</head>
<body>
<canvas id="stage"></canvas>
<span id="status">
<span id="pos"></span>
<span id="online"></span>
</span>
<script>
window.polyfill = {};
polyfill.movement = function(ev) {
ev.movementX = ev.movementX || ev.mozMovementX || ev.webkitMovementX;
ev.movementY = ev.movementY || ev.mozMovementY || ev.webkitMovementY;
return ev;
};
Math.sign = Math.sign || function(n) {
if (n < 0) {
return -1;
} else if (n > 0) {
return 1;
} else {
return 0;
}
};
</script>
<script>
window.pixl = {};
pixl.stage = document.querySelector("#stage");
pixl.ctx = stage.getContext("2d");
pixl.status = { pos: document.querySelector("#pos"),
online: document.querySelector("#online") };
pixl.window = {w: window.innerWidth, h: window.innerHeight};
pixl.world = {};
pixl.pos = {x: 0, y: 0};
pixl.color = "black";
pixl.size = 20;
pixl.stage.width = pixl.window.w;
pixl.stage.height = pixl.window.h;
pixl.to_world = function(screen_pos) {
return {x: Math.floor((screen_pos.x - pixl.window.w / 2 + pixl.pos.x * pixl.size) / pixl.size),
y: Math.floor((screen_pos.y - pixl.window.h / 2 + pixl.pos.y * pixl.size) / pixl.size)};
}
pixl.to_screen = function(world_pos) {
return {x: pixl.window.w / 2 + (world_pos.x - pixl.pos.x) * pixl.size,
y: pixl.window.h / 2 + (world_pos.y - pixl.pos.y) * pixl.size};
}
pixl.area = {};
pixl.area.w2 = function() { return Math.round(pixl.window.w / pixl.size / 2); };
pixl.area.h2 = function() { return Math.round(pixl.window.h / pixl.size / 2); };
pixl.at = function(pos) {
var p = pixl.world[pos.x + "," + pos.y];
return p !== undefined && p.color !== "white";
}
pixl.draw_pixl = function(pos, color, options) {
var color = color || pixl.color;
pixl.ctx.fillStyle = color;
var options = options || {};
var pos = options.rawValue ? pos : {x: Math.round(pos.x), y: Math.round(pos.y)};
var screen_pos = pixl.to_screen(pos);
pixl.ctx.fillRect(screen_pos.x, screen_pos.y, pixl.size, pixl.size);
pixl.world[pos.x + "," + pos.y] = {color: color};
var send = options.hasOwnProperty('send') ? options.send : true;
if (pixl.online && send) {
pixl.ws.send(JSON.stringify([{x: pos.x, y: pos.y, color: color}]));
}
}
pixl.redraw = function() {
pixl.ctx.clearRect(0, 0, pixl.window.w, pixl.window.h);
var w2 = Math.round(pixl.window.w / pixl.size / 2);
var h2 = Math.round(pixl.window.h / pixl.size / 2);
for (var x = pixl.pos.x - w2; x < pixl.pos.x + w2; x++) {
for (var y = pixl.pos.y - h2; y < pixl.pos.y + h2; y++) {
var pt = pixl.world[[x, y]];
if (pt !== undefined) {
pixl.draw_pixl({x: x, y: y}, pt.color, {send: false});
}
}
}
}
pixl.redraw();
pixl.stage.addEventListener("wheel", function(ev) {
ev.preventDefault();
pixl.size += (pixl.size / 10) * Math.sign(ev.deltaY);
if (pixl.size < 5) {
pixl.size = 5;
} else if (pixl.size > pixl.window.w / 2) {
pixl.size = Math.round(pixl.window.w / 2);
}
requestAnimationFrame(pixl.redraw);
});
window.addEventListener("resize", function(ev) {
pixl.window.w = window.innerWidth;
pixl.window.h = window.innerHeight;
pixl.stage.width = pixl.window.w;
pixl.stage.height = pixl.window.h;
pixl.redraw();
});
pixl.status.online.addEventListener("click", function(ev) {
if (pixl.online) {
pixl.disconnect();
} else {
pixl.connect();
}
});
pixl.drag = {start: undefined, current: undefined};
pixl.stage.addEventListener("mousedown", function(ev) {
/*if (document.mozPointerLockElement === null) {
document.addEventListener("mozpointerlockerror", console.log.bind(console));
pixl.stage.mozRequestPointerLock();
}*/
pixl.drag.start = {x: ev.clientX, y: ev.clientY};
pixl.drag.pos = pixl.pos;
});
pixl.stage.addEventListener("mousemove", function(ev) {
if (pixl.drag.start !== undefined) {
pixl.drag.current = {x: ev.clientX, y: ev.clientY};
}
var hovered = pixl.to_world({x: ev.clientX, y: ev.clientY});
pixl.status.pos.textContent = hovered.x + "," + hovered.y;
});
pixl.stage.addEventListener("mouseup", function(ev) {
if (pixl.drag.current === undefined) {
var ev = polyfill.movement(ev);
var world_pos = pixl.to_world({x: ev.clientX, y: ev.clientY});
pixl.draw_pixl(world_pos, pixl.at(world_pos) ? "white" : pixl.color);
} else {
console.error("drag not implemented");
}
pixl.drag = {};
});
document.addEventListener("keydown", function(ev) {
if (ev.keyCode >= 37 && ev.keyCode <= 40) {
switch (ev.keyCode) {
case 37: // left
pixl.pos.x -= 1;
break;
case 38: // up
pixl.pos.y -= 1;
break;
case 39: // right
pixl.pos.x += 1;
break;
case 40: // down
pixl.pos.y += 1;
break;
}
pixl.redraw();
}
});
pixl.online = false;
pixl.connect = function() {
var protocol = location.protocol == 'https:' ? 'wss://' : 'ws://';
pixl.ws = new WebSocket(protocol + (location.host || 'localhost:8001') + '/ws');
pixl.ws.onopen = function() {
pixl.online = true;
pixl.status.online.style.backgroundColor = "green";
};
pixl.ws.onclose = function() {
pixl.online = false;
pixl.status.online.style.backgroundColor = "red";
};
pixl.ws.onmessage = function(msg) {
var obj = JSON.parse(msg.data);
if (obj.length !== undefined) {
obj.forEach(function(p) {
pixl.draw_pixl(p, p.color, {send: false});
});
} else {
pixl.world = obj;
pixl.redraw();
}
};
};
pixl.disconnect = function () {
pixl.ws.close();
};
pixl.connect();
pixl.disconnectAndClear = function() {
pixl.disconnect();
pixl.world = {};
pixl.redraw();
}
</script>
<script>
pixl.scripts = {};
pixl.scripts.stringify = function(name, scriptObj) {
var script = "window." + name + " = " + "{};\n";
for (var fnName in scriptObj) {
var fn = scriptObj[fnName];
script += name + "." + fnName + " = " + fn.toString() + ";\n";
if (fn.doc) {
script += name + "." + fnName + ".doc = " + JSON.stringify(fn.doc) + ";\n";
}
}
return script;
};
pixl.scripts.save = function(name, scriptObj) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/scripts/' + name);
xhr.send(pixl.scripts.stringify(name, scriptObj));
};
pixl.scripts.load = function(name) {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/scripts/' + name);
xhr.onreadystatechange = function(ev) {
if (xhr.readyState == 4) {
var code = xhr.responseText;
eval(code);
}
}
xhr.send();
};
pixl.scripts.list = function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/scripts');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var scripts = xhr.responseText.split("\n");
for (var i = 0; i < scripts.length; i++) {
console.log(scripts[i]);
}
}
};
xhr.send();
}
</script>
</body>
</html>
|