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
226
css/index.css
226
css/index.css
|
@ -1,187 +1,191 @@
|
|||
* {
|
||||
font-size: 100%;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 100%;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.backgroundCanvas {
|
||||
background-color: #ccc;
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.maskPaintCanvas {
|
||||
border: 3px dotted #993355C0
|
||||
border: 3px dotted #993355c0;
|
||||
}
|
||||
|
||||
.overlayCanvas {
|
||||
border: 1px solid #F00;
|
||||
border: 1px solid #f00;
|
||||
}
|
||||
|
||||
.tempCanvas {
|
||||
border: 3px dotted #007AFFC0;
|
||||
border: 3px dotted #007affc0;
|
||||
}
|
||||
|
||||
.targetCanvas {
|
||||
border: 2px dashed #0F0;
|
||||
border: 2px dashed #0f0;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
border: 2px dotted #00F;
|
||||
border: 2px dotted #00f;
|
||||
}
|
||||
|
||||
.mainHSplit {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
grid-column-gap: 5px;
|
||||
grid-row-gap: 5px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
grid-column-gap: 5px;
|
||||
grid-row-gap: 5px;
|
||||
}
|
||||
|
||||
.uiWrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 15fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-column-gap: 5px;
|
||||
grid-row-gap: 5px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 15fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-column-gap: 5px;
|
||||
grid-row-gap: 5px;
|
||||
}
|
||||
|
||||
#infoContainer {
|
||||
position: absolute;
|
||||
width: 250px;
|
||||
height: auto;
|
||||
z-index: 999;
|
||||
|
||||
}
|
||||
#draggable{
|
||||
cursor:move
|
||||
.uiContainer {
|
||||
position: absolute;
|
||||
width: 250px;
|
||||
height: auto;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
#DraggableTitleBar {
|
||||
z-index: 999;
|
||||
cursor: move;
|
||||
background-color: rgba(104, 104, 104, 0.75);
|
||||
.uiTitleBar {
|
||||
z-index: 999;
|
||||
cursor: move;
|
||||
background-color: rgba(104, 104, 104, 0.75);
|
||||
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: auto;
|
||||
font-size: 1.5em;
|
||||
color: black;
|
||||
text-align: center;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
border: solid;
|
||||
border-bottom: none;
|
||||
border-color: black;
|
||||
user-select: none;
|
||||
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: auto;
|
||||
font-size: 1.5em;
|
||||
color: black;
|
||||
text-align: center;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
border: solid;
|
||||
border-bottom: none;
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
.draggable {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.toolbar > .tool {
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.toolbar > .tool:not(:last-child) {
|
||||
margin-right: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
button.tool {
|
||||
background-color: rgb(0, 0, 50);
|
||||
color: rgb(255, 255, 255);
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
font-size: 15px;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
background-color: rgb(0, 0, 50);
|
||||
color: rgb(255, 255, 255);
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
font-size: 15px;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
button.tool:hover {
|
||||
background-color: #667;
|
||||
background-color: #667;
|
||||
}
|
||||
|
||||
.collapsible {
|
||||
background-color: rgb(0, 0, 0);
|
||||
color: rgb(255, 255, 255);
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
border: none;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
font-size: 15px;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
background-color: rgb(0, 0, 0);
|
||||
color: rgb(255, 255, 255);
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
border: none;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
font-size: 15px;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.collapsible:hover {
|
||||
background-color: #777;
|
||||
background-color: #777;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.2s ease-out;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.2s ease-out;
|
||||
}
|
||||
|
||||
.info {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
|
||||
color: black;
|
||||
border: solid;
|
||||
border-top: none;
|
||||
border-color: black;
|
||||
font-size: medium;
|
||||
text-align: left;
|
||||
max-height: fit-content;
|
||||
overflow: auto;
|
||||
cursor: auto;
|
||||
color: black;
|
||||
border: solid;
|
||||
border-top: none;
|
||||
border-color: black;
|
||||
font-size: medium;
|
||||
text-align: left;
|
||||
max-height: fit-content;
|
||||
overflow: auto;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
|
||||
.canvasHolder {
|
||||
position: relative;
|
||||
width: 2560px;
|
||||
height: 1440px;
|
||||
position: relative;
|
||||
width: 2560px;
|
||||
height: 1440px;
|
||||
}
|
||||
|
||||
.mainCanvases {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 2560px;
|
||||
height: 1440px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 2560px;
|
||||
height: 1440px;
|
||||
}
|
||||
|
||||
.masks {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: 1fr;
|
||||
grid-column-gap: 0px;
|
||||
grid-row-gap: 0px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: 1fr;
|
||||
grid-column-gap: 0px;
|
||||
grid-row-gap: 0px;
|
||||
}
|
||||
|
||||
.maskCanvasMonitor .overMaskCanvasMonitor .initImgCanvasMonitor {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.maskPaintCanvas {
|
||||
filter: opacity(40%);
|
||||
}
|
||||
|
||||
.strokeText {
|
||||
-webkit-text-stroke: 1px #888;
|
||||
font-size: 150%;
|
||||
color: #000;
|
||||
}
|
||||
-webkit-text-stroke: 1px #888;
|
||||
font-size: 150%;
|
||||
color: #000;
|
||||
}
|
||||
|
|
21
index.html
21
index.html
|
@ -9,8 +9,9 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<div id="infoContainer">
|
||||
<div id="DraggableTitleBar" class="draggable">openOutpaint 🐠</div>
|
||||
<!-- Main Toolbar -->
|
||||
<div id="infoContainer" class="uiContainer">
|
||||
<div id="infoTitleBar" class="draggable uiTitleBar">openOutpaint 🐠</div>
|
||||
<div id="info" class="info" style="min-width:200px;">
|
||||
|
||||
<label for="host">Host</label>
|
||||
|
@ -69,9 +70,6 @@
|
|||
<input type="checkbox" id="cbxEnableErasing" onchange="changeEnableErasing()"><br />
|
||||
<label for="cbxPaint">Mask mode?</label>
|
||||
<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>
|
||||
<input type="checkbox" id="cbxHRFix" onchange="changeHiResFix()"><br />
|
||||
|
@ -116,6 +114,17 @@
|
|||
<br />
|
||||
<hr>
|
||||
</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">
|
||||
<button type="button" onclick="commands.undo()" class="tool">undo</button>
|
||||
<button type="button" onclick="commands.redo()" class="tool">redo</button>
|
||||
|
@ -176,6 +185,8 @@
|
|||
</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/index.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
|
||||
); //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) {
|
||||
const rect = ovCanvas.getBoundingClientRect();
|
||||
var oddOffset = 0;
|
||||
|
@ -496,14 +503,7 @@ function mouseDown(evt) {
|
|||
nextBox.w = arbitraryImageData.width;
|
||||
nextBox.h = arbitraryImageData.height;
|
||||
dropTargets.push(nextBox);
|
||||
} 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 {
|
||||
} else if (!paintMode) {
|
||||
//const rect = ovCanvas.getBoundingClientRect()
|
||||
var nextBox = {};
|
||||
nextBox.x =
|
||||
|
@ -738,6 +738,7 @@ function changePaintMode() {
|
|||
}
|
||||
|
||||
function changeEnableErasing() {
|
||||
// yeah because this is for the image layer
|
||||
enableErasing = document.getElementById("cbxEnableErasing").checked;
|
||||
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) {
|
||||
var p1 = 0,
|
||||
p2 = 0,
|
||||
p3 = 0,
|
||||
var p3 = 0,
|
||||
p4 = 0;
|
||||
var draggableElements = document.getElementsByClassName("draggable");
|
||||
var draggableElements = elmnt.getElementsByClassName("draggable");
|
||||
for (var i = 0; i < draggableElements.length; i++) {
|
||||
draggableElements[i].onmousedown = dragMouseDown;
|
||||
}
|
||||
|
@ -20,8 +19,8 @@ function dragElement(elmnt) {
|
|||
|
||||
function elementDrag(e) {
|
||||
e.preventDefault();
|
||||
p1 = p3 - e.clientX;
|
||||
p2 = p4 - e.clientY;
|
||||
elmnt.style.bottom = null;
|
||||
elmnt.style.right = null;
|
||||
elmnt.style.top = elmnt.offsetTop - (p4 - e.clientY) + "px";
|
||||
elmnt.style.left = elmnt.offsetLeft - (p3 - e.clientX) + "px";
|
||||
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");
|
||||
for (var i = 0; i < coll.length; i++) {
|
||||
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