Merge pull request #22 from seijihariki/edit_utils
mouse input handler - input.js - proper solid mask
This commit is contained in:
commit
2efb01059c
6 changed files with 546 additions and 165 deletions
|
@ -12,23 +12,23 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.maskPaintCanvas {
|
.maskPaintCanvas {
|
||||||
border: 3px dotted #993355C0
|
border: 3px dotted #993355c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayCanvas {
|
.overlayCanvas {
|
||||||
border: 1px solid #F00;
|
border: 1px solid #f00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tempCanvas {
|
.tempCanvas {
|
||||||
border: 3px dotted #007AFFC0;
|
border: 3px dotted #007affc0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.targetCanvas {
|
.targetCanvas {
|
||||||
border: 2px dashed #0F0;
|
border: 2px dashed #0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas {
|
.canvas {
|
||||||
border: 2px dotted #00F;
|
border: 2px dotted #00f;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mainHSplit {
|
.mainHSplit {
|
||||||
|
@ -45,25 +45,22 @@
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
grid-column-gap: 5px;
|
grid-column-gap: 5px;
|
||||||
grid-row-gap: 5px;
|
grid-row-gap: 5px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#infoContainer {
|
.uiContainer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
height: auto;
|
height: auto;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
|
|
||||||
}
|
|
||||||
#draggable{
|
|
||||||
cursor:move
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#DraggableTitleBar {
|
.uiTitleBar {
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
cursor: move;
|
cursor: move;
|
||||||
background-color: rgba(104, 104, 104, 0.75);
|
background-color: rgba(104, 104, 104, 0.75);
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
|
@ -79,6 +76,10 @@
|
||||||
border-color: black;
|
border-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.draggable {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -153,7 +154,6 @@ button.tool:hover {
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.canvasHolder {
|
.canvasHolder {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 2560px;
|
width: 2560px;
|
||||||
|
@ -180,6 +180,10 @@ button.tool:hover {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maskPaintCanvas {
|
||||||
|
filter: opacity(40%);
|
||||||
|
}
|
||||||
|
|
||||||
.strokeText {
|
.strokeText {
|
||||||
-webkit-text-stroke: 1px #888;
|
-webkit-text-stroke: 1px #888;
|
||||||
font-size: 150%;
|
font-size: 150%;
|
||||||
|
|
21
index.html
21
index.html
|
@ -9,8 +9,9 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="infoContainer">
|
<!-- Main Toolbar -->
|
||||||
<div id="DraggableTitleBar" class="draggable">openOutpaint 🐠</div>
|
<div id="infoContainer" class="uiContainer">
|
||||||
|
<div id="infoTitleBar" class="draggable uiTitleBar">openOutpaint 🐠</div>
|
||||||
<div id="info" class="info" style="min-width:200px;">
|
<div id="info" class="info" style="min-width:200px;">
|
||||||
|
|
||||||
<label for="host">Host</label>
|
<label for="host">Host</label>
|
||||||
|
@ -69,9 +70,6 @@
|
||||||
<input type="checkbox" id="cbxEnableErasing" onchange="changeEnableErasing()"><br />
|
<input type="checkbox" id="cbxEnableErasing" onchange="changeEnableErasing()"><br />
|
||||||
<label for="cbxPaint">Mask mode?</label>
|
<label for="cbxPaint">Mask mode?</label>
|
||||||
<input type="checkbox" id="cbxPaint" onchange="changePaintMode()"><br />
|
<input type="checkbox" id="cbxPaint" onchange="changePaintMode()"><br />
|
||||||
<!-- Having to tick a box to erase is a bad user experience, same goes for image erasing( cold be remedied by a temp confirm div) -->
|
|
||||||
<!-- <label for="cbxErase"><s>Erase mask?</s></label>
|
|
||||||
<input type="checkbox" id="cbxErase" onchange="changeEraseMode()" disabled="disabled"><br /> -->
|
|
||||||
|
|
||||||
<label for="cbxHRFix">Auto txt2img HRfix?</label>
|
<label for="cbxHRFix">Auto txt2img HRfix?</label>
|
||||||
<input type="checkbox" id="cbxHRFix" onchange="changeHiResFix()"><br />
|
<input type="checkbox" id="cbxHRFix" onchange="changeHiResFix()"><br />
|
||||||
|
@ -116,6 +114,17 @@
|
||||||
<br />
|
<br />
|
||||||
<hr>
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- History Toolbar -->
|
||||||
|
<div id="historyContainer" class="uiContainer" style="right: 0;">
|
||||||
|
<div id="historyTitleBar" class="draggable uiTitleBar">History</div>
|
||||||
|
<div class="info" style="min-width:200px;">
|
||||||
|
<div id="history" class="history">
|
||||||
|
|
||||||
|
</div>
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<button type="button" onclick="commands.undo()" class="tool">undo</button>
|
<button type="button" onclick="commands.undo()" class="tool">undo</button>
|
||||||
<button type="button" onclick="commands.redo()" class="tool">redo</button>
|
<button type="button" onclick="commands.redo()" class="tool">redo</button>
|
||||||
|
@ -176,6 +185,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="js/util.js" type="text/javascript"></script>
|
||||||
|
<script src="js/input.js" type="text/javascript"></script>
|
||||||
<script src="js/commands.js" type="text/javascript"></script>
|
<script src="js/commands.js" type="text/javascript"></script>
|
||||||
<script src="js/index.js" type="text/javascript"></script>
|
<script src="js/index.js" type="text/javascript"></script>
|
||||||
<script src="js/settingsbar.js" type="text/javascript"></script>
|
<script src="js/settingsbar.js" type="text/javascript"></script>
|
||||||
|
|
85
js/index.js
85
js/index.js
|
@ -444,43 +444,50 @@ function mouseMove(evt) {
|
||||||
basePixelCount * scaleFactor,
|
basePixelCount * scaleFactor,
|
||||||
basePixelCount * scaleFactor
|
basePixelCount * scaleFactor
|
||||||
); //origin is middle of the frame
|
); //origin is middle of the frame
|
||||||
} else {
|
|
||||||
// draw big translucent red blob cursor
|
|
||||||
ovCtx.beginPath();
|
|
||||||
ovCtx.arc(canvasX, canvasY, 4 * scaleFactor, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 8x on a line???
|
|
||||||
ovCtx.fillStyle = "#FF6A6A50";
|
|
||||||
ovCtx.fill();
|
|
||||||
// in case i'm trying to draw
|
|
||||||
mouseX = parseInt(evt.clientX - canvasOffsetX);
|
|
||||||
mouseY = parseInt(evt.clientY - canvasOffsetY);
|
|
||||||
if (clicked) {
|
|
||||||
// i'm trying to draw, please draw :(
|
|
||||||
maskPaintCtx.globalCompositeOperation = "source-over";
|
|
||||||
maskPaintCtx.strokeStyle = "#FF6A6A10";
|
|
||||||
maskPaintCtx.lineWidth = 8 * scaleFactor;
|
|
||||||
maskPaintCtx.beginPath();
|
|
||||||
maskPaintCtx.moveTo(prevMouseX, prevMouseY);
|
|
||||||
maskPaintCtx.lineTo(mouseX, mouseY);
|
|
||||||
maskPaintCtx.lineJoin = maskPaintCtx.lineCap = "round";
|
|
||||||
maskPaintCtx.stroke();
|
|
||||||
}
|
|
||||||
// Erase mask if right button is held
|
|
||||||
// no reason to have to tick a checkbox for this, more intuitive for both erases (mask and actual images) to just work on right click and inform the user about it
|
|
||||||
if (evt.buttons == 2) {
|
|
||||||
maskPaintCtx.globalCompositeOperation = "destination-out";
|
|
||||||
maskPaintCtx.beginPath();
|
|
||||||
maskPaintCtx.strokeStyle = "#FFFFFFFF";
|
|
||||||
maskPaintCtx.lineWidth = 8 * scaleFactor;
|
|
||||||
maskPaintCtx.moveTo(prevMouseX, prevMouseY);
|
|
||||||
maskPaintCtx.lineTo(mouseX, mouseY);
|
|
||||||
maskPaintCtx.lineJoin = maskPaintCtx.lineCap = "round";
|
|
||||||
maskPaintCtx.stroke();
|
|
||||||
}
|
|
||||||
prevMouseX = mouseX;
|
|
||||||
prevMouseY = mouseY;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mask implementation
|
||||||
|
*/
|
||||||
|
mouse.listen.canvas.onmousemove.on((evn) => {
|
||||||
|
if (paintMode && evn.target.id === "overlayCanvas") {
|
||||||
|
// draw big translucent red blob cursor
|
||||||
|
ovCtx.beginPath();
|
||||||
|
ovCtx.arc(evn.x, evn.y, 4 * scaleFactor, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 8x on a line???
|
||||||
|
ovCtx.fillStyle = "#FF6A6A50";
|
||||||
|
ovCtx.fill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mouse.listen.canvas.left.onpaint.on((evn) => {
|
||||||
|
if (paintMode && evn.initialTarget.id === "overlayCanvas") {
|
||||||
|
maskPaintCtx.globalCompositeOperation = "source-over";
|
||||||
|
maskPaintCtx.strokeStyle = "#FF6A6A";
|
||||||
|
|
||||||
|
maskPaintCtx.lineWidth = 8 * scaleFactor;
|
||||||
|
maskPaintCtx.beginPath();
|
||||||
|
maskPaintCtx.moveTo(evn.px, evn.py);
|
||||||
|
maskPaintCtx.lineTo(evn.x, evn.y);
|
||||||
|
maskPaintCtx.lineJoin = maskPaintCtx.lineCap = "round";
|
||||||
|
maskPaintCtx.stroke();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mouse.listen.canvas.right.onpaint.on((evn) => {
|
||||||
|
if (paintMode && evn.initialTarget.id === "overlayCanvas") {
|
||||||
|
maskPaintCtx.globalCompositeOperation = "destination-out";
|
||||||
|
maskPaintCtx.strokeStyle = "#FFFFFFFF";
|
||||||
|
|
||||||
|
maskPaintCtx.lineWidth = 8 * scaleFactor;
|
||||||
|
maskPaintCtx.beginPath();
|
||||||
|
maskPaintCtx.moveTo(evn.px, evn.py);
|
||||||
|
maskPaintCtx.lineTo(evn.x, evn.y);
|
||||||
|
maskPaintCtx.lineJoin = maskPaintCtx.lineCap = "round";
|
||||||
|
maskPaintCtx.stroke();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function mouseDown(evt) {
|
function mouseDown(evt) {
|
||||||
const rect = ovCanvas.getBoundingClientRect();
|
const rect = ovCanvas.getBoundingClientRect();
|
||||||
var oddOffset = 0;
|
var oddOffset = 0;
|
||||||
|
@ -496,14 +503,7 @@ function mouseDown(evt) {
|
||||||
nextBox.w = arbitraryImageData.width;
|
nextBox.w = arbitraryImageData.width;
|
||||||
nextBox.h = arbitraryImageData.height;
|
nextBox.h = arbitraryImageData.height;
|
||||||
dropTargets.push(nextBox);
|
dropTargets.push(nextBox);
|
||||||
} else if (paintMode) {
|
} else if (!paintMode) {
|
||||||
//const rect = ovCanvas.getBoundingClientRect() // not-quite pixel offset was driving me insane
|
|
||||||
const canvasOffsetX = rect.left;
|
|
||||||
const canvasOffsetY = rect.top;
|
|
||||||
prevMouseX = mouseX = evt.clientX - canvasOffsetX;
|
|
||||||
prevMouseY = mouseY = evt.clientY - canvasOffsetY;
|
|
||||||
clicked = true;
|
|
||||||
} else {
|
|
||||||
//const rect = ovCanvas.getBoundingClientRect()
|
//const rect = ovCanvas.getBoundingClientRect()
|
||||||
var nextBox = {};
|
var nextBox = {};
|
||||||
nextBox.x =
|
nextBox.x =
|
||||||
|
@ -738,6 +738,7 @@ function changePaintMode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeEnableErasing() {
|
function changeEnableErasing() {
|
||||||
|
// yeah because this is for the image layer
|
||||||
enableErasing = document.getElementById("cbxEnableErasing").checked;
|
enableErasing = document.getElementById("cbxEnableErasing").checked;
|
||||||
localStorage.setItem("enable_erase", enableErasing);
|
localStorage.setItem("enable_erase", enableErasing);
|
||||||
}
|
}
|
||||||
|
|
303
js/input.js
Normal file
303
js/input.js
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
const inputConfig = {
|
||||||
|
clickRadius: 10, // Radius to be considered a click (pixels). If farther, turns into a drag
|
||||||
|
clickTiming: 500, // Timing window to be considered a click (ms). If longer, turns into a drag
|
||||||
|
dClickTiming: 500, // Timing window to be considered a double click (ms).
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mouse input processing
|
||||||
|
*/
|
||||||
|
// Base object generator functions
|
||||||
|
function _context_coords() {
|
||||||
|
return {
|
||||||
|
dragging: {
|
||||||
|
left: null,
|
||||||
|
middle: null,
|
||||||
|
right: null,
|
||||||
|
},
|
||||||
|
|
||||||
|
prev: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
pos: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function _mouse_observers() {
|
||||||
|
return {
|
||||||
|
// Simple click handlers
|
||||||
|
onclick: new Observer(),
|
||||||
|
// Double click handlers (will still trigger simple click handler as well)
|
||||||
|
ondclick: new Observer(),
|
||||||
|
// Drag handler
|
||||||
|
ondragstart: new Observer(),
|
||||||
|
ondrag: new Observer(),
|
||||||
|
ondragend: new Observer(),
|
||||||
|
// Paint handler (like drag handler, but with no delay); will trigger during clicks too
|
||||||
|
onpaintstart: new Observer(),
|
||||||
|
onpaint: new Observer(),
|
||||||
|
onpaintend: new Observer(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function _context_observers() {
|
||||||
|
return {
|
||||||
|
onmousemove: new Observer(),
|
||||||
|
left: _mouse_observers(),
|
||||||
|
middle: _mouse_observers(),
|
||||||
|
right: _mouse_observers(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const mouse = {
|
||||||
|
buttons: {
|
||||||
|
right: null,
|
||||||
|
left: null,
|
||||||
|
middle: null,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Mouse Actions in Window Coordinates
|
||||||
|
window: _context_coords(),
|
||||||
|
|
||||||
|
// Mouse Actions in Canvas Coordinates
|
||||||
|
canvas: _context_coords(),
|
||||||
|
|
||||||
|
// Mouse Actions in World Coordinates
|
||||||
|
world: _context_coords(),
|
||||||
|
|
||||||
|
listen: {
|
||||||
|
window: _context_observers(),
|
||||||
|
canvas: _context_observers(),
|
||||||
|
world: _context_observers(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function _mouse_state_snapshot() {
|
||||||
|
return {
|
||||||
|
buttons: window.structuredClone(mouse.buttons),
|
||||||
|
window: window.structuredClone(mouse.window),
|
||||||
|
canvas: window.structuredClone(mouse.canvas),
|
||||||
|
world: window.structuredClone(mouse.world),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const _double_click_timeout = {};
|
||||||
|
const _drag_start_timeout = {};
|
||||||
|
|
||||||
|
window.onmousedown = (evn) => {
|
||||||
|
const time = new Date();
|
||||||
|
|
||||||
|
// Processes for a named button
|
||||||
|
const onhold = (key) => () => {
|
||||||
|
if (_double_click_timeout[key]) {
|
||||||
|
// ondclick event
|
||||||
|
["window", "canvas", "world"].forEach((ctx) =>
|
||||||
|
mouse.listen[ctx][key].ondclick.emit({
|
||||||
|
target: evn.target,
|
||||||
|
buttonId: evn.button,
|
||||||
|
x: mouse[ctx].pos.x,
|
||||||
|
y: mouse[ctx].pos.y,
|
||||||
|
timestamp: new Date(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Start timer
|
||||||
|
_double_click_timeout[key] = setTimeout(
|
||||||
|
() => delete _double_click_timeout[key],
|
||||||
|
inputConfig.dClickTiming
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set drag start timeout
|
||||||
|
_drag_start_timeout[key] = setTimeout(() => {
|
||||||
|
["window", "canvas", "world"].forEach((ctx) => {
|
||||||
|
mouse.listen[ctx][key].ondragstart.emit({
|
||||||
|
target: evn.target,
|
||||||
|
buttonId: evn.button,
|
||||||
|
x: mouse[ctx].pos.x,
|
||||||
|
y: mouse[ctx].pos.y,
|
||||||
|
timestamp: new Date(),
|
||||||
|
});
|
||||||
|
if (mouse[ctx].dragging[key]) mouse[ctx].dragging[key].drag = true;
|
||||||
|
|
||||||
|
delete _drag_start_timeout[key];
|
||||||
|
});
|
||||||
|
}, inputConfig.clickTiming);
|
||||||
|
|
||||||
|
["window", "canvas", "world"].forEach((ctx) => {
|
||||||
|
mouse.buttons[key] = time;
|
||||||
|
mouse[ctx].dragging[key] = {target: evn.target};
|
||||||
|
Object.assign(mouse[ctx].dragging[key], mouse[ctx].pos);
|
||||||
|
|
||||||
|
// onpaintstart event
|
||||||
|
mouse.listen[ctx][key].onpaintstart.emit({
|
||||||
|
target: evn.target,
|
||||||
|
buttonId: evn.button,
|
||||||
|
x: mouse[ctx].pos.x,
|
||||||
|
y: mouse[ctx].pos.y,
|
||||||
|
timestamp: new Date(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Runs the correct handler
|
||||||
|
const buttons = [onhold("left"), onhold("middle"), onhold("right")];
|
||||||
|
|
||||||
|
buttons[evn.button] && buttons[evn.button]();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onmouseup = (evn) => {
|
||||||
|
const time = new Date();
|
||||||
|
|
||||||
|
// Processes for a named button
|
||||||
|
const onrelease = (key) => () => {
|
||||||
|
["window", "canvas", "world"].forEach((ctx) => {
|
||||||
|
const start = {
|
||||||
|
x: mouse[ctx].dragging[key].x,
|
||||||
|
y: mouse[ctx].dragging[key].y,
|
||||||
|
};
|
||||||
|
|
||||||
|
// onclick event
|
||||||
|
const dx = mouse[ctx].pos.x - start.x;
|
||||||
|
const dy = mouse[ctx].pos.y - start.y;
|
||||||
|
|
||||||
|
if (
|
||||||
|
time.getTime() - mouse.buttons[key].getTime() <
|
||||||
|
inputConfig.clickTiming &&
|
||||||
|
dx * dx + dy * dy < inputConfig.clickRadius * inputConfig.clickRadius
|
||||||
|
)
|
||||||
|
mouse.listen[ctx][key].onclick.emit({
|
||||||
|
target: evn.target,
|
||||||
|
buttonId: evn.button,
|
||||||
|
x: mouse[ctx].pos.x,
|
||||||
|
y: mouse[ctx].pos.y,
|
||||||
|
timestamp: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// onpaintend event
|
||||||
|
mouse.listen[ctx][key].onpaintend.emit({
|
||||||
|
target: evn.target,
|
||||||
|
initialTarget: mouse[ctx].dragging[key].target,
|
||||||
|
buttonId: evn.button,
|
||||||
|
x: mouse[ctx].pos.x,
|
||||||
|
y: mouse[ctx].pos.y,
|
||||||
|
timestamp: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ondragend event
|
||||||
|
if (mouse[ctx].dragging[key].drag)
|
||||||
|
mouse.listen[ctx][key].ondragend.emit({
|
||||||
|
target: evn.target,
|
||||||
|
initialTarget: mouse[ctx].dragging[key].target,
|
||||||
|
buttonId: evn.button,
|
||||||
|
x: mouse[ctx].pos.x,
|
||||||
|
y: mouse[ctx].pos.y,
|
||||||
|
timestamp: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
mouse[ctx].dragging[key] = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_drag_start_timeout[key] !== undefined) {
|
||||||
|
clearTimeout(_drag_start_timeout[key]);
|
||||||
|
delete _drag_start_timeout[key];
|
||||||
|
}
|
||||||
|
mouse.buttons[key] = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Runs the correct handler
|
||||||
|
const buttons = [onrelease("left"), onrelease("middle"), onrelease("right")];
|
||||||
|
|
||||||
|
buttons[evn.button] && buttons[evn.button]();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onmousemove = (evn) => {
|
||||||
|
// Set Window Coordinates
|
||||||
|
Object.assign(mouse.window.prev, mouse.window.pos);
|
||||||
|
mouse.window.pos = {x: evn.clientX, y: evn.clientY};
|
||||||
|
|
||||||
|
// Set Canvas Coordinates (using overlay canvas as reference)
|
||||||
|
if (evn.target.id === "overlayCanvas") {
|
||||||
|
Object.assign(mouse.canvas.prev, mouse.canvas.pos);
|
||||||
|
mouse.canvas.pos = {x: evn.layerX, y: evn.layerY};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set World Coordinates (For now the same as canvas coords; Will be useful with infinite canvas)
|
||||||
|
if (evn.target.id === "overlayCanvas") {
|
||||||
|
Object.assign(mouse.world.prev, mouse.world.pos);
|
||||||
|
mouse.world.pos = {x: evn.layerX, y: evn.layerY};
|
||||||
|
}
|
||||||
|
|
||||||
|
["window", "canvas", "world"].forEach((ctx) => {
|
||||||
|
mouse.listen[ctx].onmousemove.emit({
|
||||||
|
target: evn.target,
|
||||||
|
px: mouse[ctx].prev.x,
|
||||||
|
py: mouse[ctx].prev.y,
|
||||||
|
x: mouse[ctx].pos.x,
|
||||||
|
y: mouse[ctx].pos.y,
|
||||||
|
timestamp: new Date(),
|
||||||
|
});
|
||||||
|
["left", "middle", "right"].forEach((key) => {
|
||||||
|
// ondrag event
|
||||||
|
if (mouse[ctx].dragging[key] && mouse[ctx].dragging[key].drag)
|
||||||
|
mouse.listen[ctx][key].ondrag.emit({
|
||||||
|
target: evn.target,
|
||||||
|
initialTarget: mouse[ctx].dragging[key].target,
|
||||||
|
px: mouse[ctx].prev.x,
|
||||||
|
py: mouse[ctx].prev.y,
|
||||||
|
x: mouse[ctx].pos.x,
|
||||||
|
y: mouse[ctx].pos.y,
|
||||||
|
timestamp: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// onpaint event
|
||||||
|
if (mouse[ctx].dragging[key])
|
||||||
|
mouse.listen[ctx][key].onpaint.emit({
|
||||||
|
target: evn.target,
|
||||||
|
initialTarget: mouse[ctx].dragging[key].target,
|
||||||
|
px: mouse[ctx].prev.x,
|
||||||
|
py: mouse[ctx].prev.y,
|
||||||
|
x: mouse[ctx].pos.x,
|
||||||
|
y: mouse[ctx].pos.y,
|
||||||
|
timestamp: new Date(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/** MOUSE DEBUG */
|
||||||
|
/*
|
||||||
|
mouse.listen.window.right.onclick.on(() =>
|
||||||
|
console.debug('mouse.listen.window.right.onclick')
|
||||||
|
);
|
||||||
|
|
||||||
|
mouse.listen.window.right.ondclick.on(() =>
|
||||||
|
console.debug('mouse.listen.window.right.ondclick')
|
||||||
|
);
|
||||||
|
mouse.listen.window.right.ondragstart.on(() =>
|
||||||
|
console.debug('mouse.listen.window.right.ondragstart')
|
||||||
|
);
|
||||||
|
mouse.listen.window.right.ondrag.on(() =>
|
||||||
|
console.debug('mouse.listen.window.right.ondrag')
|
||||||
|
);
|
||||||
|
mouse.listen.window.right.ondragend.on(() =>
|
||||||
|
console.debug('mouse.listen.window.right.ondragend')
|
||||||
|
);
|
||||||
|
|
||||||
|
mouse.listen.window.right.onpaintstart.on(() =>
|
||||||
|
console.debug('mouse.listen.window.right.onpaintstart')
|
||||||
|
);
|
||||||
|
mouse.listen.window.right.onpaint.on(() =>
|
||||||
|
console.debug('mouse.listen.window.right.onpaint')
|
||||||
|
);
|
||||||
|
mouse.listen.window.right.onpaintend.on(() =>
|
||||||
|
console.debug('mouse.listen.window.right.onpaintend')
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mouse input processing
|
||||||
|
*/
|
|
@ -1,11 +1,10 @@
|
||||||
dragElement(document.getElementById("infoContainer"));
|
//dragElement(document.getElementById("infoContainer"));
|
||||||
|
//dragElement(document.getElementById("historyContainer"));
|
||||||
|
|
||||||
function dragElement(elmnt) {
|
function dragElement(elmnt) {
|
||||||
var p1 = 0,
|
var p3 = 0,
|
||||||
p2 = 0,
|
|
||||||
p3 = 0,
|
|
||||||
p4 = 0;
|
p4 = 0;
|
||||||
var draggableElements = document.getElementsByClassName("draggable");
|
var draggableElements = elmnt.getElementsByClassName("draggable");
|
||||||
for (var i = 0; i < draggableElements.length; i++) {
|
for (var i = 0; i < draggableElements.length; i++) {
|
||||||
draggableElements[i].onmousedown = dragMouseDown;
|
draggableElements[i].onmousedown = dragMouseDown;
|
||||||
}
|
}
|
||||||
|
@ -20,8 +19,8 @@ function dragElement(elmnt) {
|
||||||
|
|
||||||
function elementDrag(e) {
|
function elementDrag(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
p1 = p3 - e.clientX;
|
elmnt.style.bottom = null;
|
||||||
p2 = p4 - e.clientY;
|
elmnt.style.right = null;
|
||||||
elmnt.style.top = elmnt.offsetTop - (p4 - e.clientY) + "px";
|
elmnt.style.top = elmnt.offsetTop - (p4 - e.clientY) + "px";
|
||||||
elmnt.style.left = elmnt.offsetLeft - (p3 - e.clientX) + "px";
|
elmnt.style.left = elmnt.offsetLeft - (p3 - e.clientX) + "px";
|
||||||
p3 = e.clientX;
|
p3 = e.clientX;
|
||||||
|
@ -34,6 +33,42 @@ function dragElement(elmnt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeDraggable(id) {
|
||||||
|
const element = document.getElementById(id);
|
||||||
|
const startbb = element.getBoundingClientRect();
|
||||||
|
let dragging = false;
|
||||||
|
let offset = {x: 0, y: 0};
|
||||||
|
|
||||||
|
element.style.top = startbb.y + "px";
|
||||||
|
element.style.left = startbb.x + "px";
|
||||||
|
|
||||||
|
mouse.listen.window.left.onpaintstart.on((evn) => {
|
||||||
|
if (
|
||||||
|
element.contains(evn.target) &&
|
||||||
|
evn.target.classList.contains("draggable")
|
||||||
|
) {
|
||||||
|
const bb = element.getBoundingClientRect();
|
||||||
|
offset.x = evn.x - bb.x;
|
||||||
|
offset.y = evn.y - bb.y;
|
||||||
|
dragging = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mouse.listen.window.left.onpaint.on((evn) => {
|
||||||
|
if (dragging) {
|
||||||
|
element.style.top = evn.y - offset.y + "px";
|
||||||
|
element.style.left = evn.x - offset.x + "px";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mouse.listen.window.left.onpaintend.on((evn) => {
|
||||||
|
dragging = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
makeDraggable("infoContainer");
|
||||||
|
makeDraggable("historyContainer");
|
||||||
|
|
||||||
var coll = document.getElementsByClassName("collapsible");
|
var coll = document.getElementsByClassName("collapsible");
|
||||||
for (var i = 0; i < coll.length; i++) {
|
for (var i = 0; i < coll.length; i++) {
|
||||||
coll[i].addEventListener("click", function () {
|
coll[i].addEventListener("click", function () {
|
||||||
|
|
27
js/util.js
Normal file
27
js/util.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* Implementation of a simple Oberver Pattern for custom event handling
|
||||||
|
*/
|
||||||
|
function Observer() {
|
||||||
|
this.handlers = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
Observer.prototype = {
|
||||||
|
// Adds handler for this message
|
||||||
|
on(callback) {
|
||||||
|
this.handlers.add(callback);
|
||||||
|
return callback;
|
||||||
|
},
|
||||||
|
clear(callback) {
|
||||||
|
return this.handlers.delete(callback);
|
||||||
|
},
|
||||||
|
emit(msg) {
|
||||||
|
this.handlers.forEach(async (handler) => {
|
||||||
|
try {
|
||||||
|
await handler(msg);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Observer failed to run handler');
|
||||||
|
console.warn(handler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
Loading…
Reference in a new issue