ea64c138c3
input.js is now responsible for processing mouse input and translating it to relevant events. This allows for less bloat on the main logic in index.js and easy implementation of new functionality Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
284 lines
8.4 KiB
JavaScript
284 lines
8.4 KiB
JavaScript
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 {
|
|
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({
|
|
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({
|
|
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] = {};
|
|
Object.assign(mouse[ctx].dragging[key], mouse[ctx].pos);
|
|
|
|
// onpaintstart event
|
|
mouse.listen[ctx][key].onpaintstart.emit({
|
|
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({
|
|
buttonId: evn.button,
|
|
x: mouse[ctx].pos.x,
|
|
y: mouse[ctx].pos.y,
|
|
timestamp: new Date(),
|
|
});
|
|
|
|
// onpaintend event
|
|
mouse.listen[ctx][key].onpaintend.emit({
|
|
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({
|
|
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) => {
|
|
['left', 'middle', 'right'].forEach((key) => {
|
|
// ondrag event
|
|
if (mouse[ctx].dragging[key] && mouse[ctx].dragging[key].drag)
|
|
mouse.listen[ctx][key].ondrag.emit({
|
|
px: mouse[ctx].prev.x,
|
|
py: mouse[ctx].prev.x,
|
|
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({
|
|
px: mouse[ctx].prev.x,
|
|
py: mouse[ctx].prev.x,
|
|
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')
|
|
);
|
|
*/
|