awv3
Version:
⚡ AWV3 embedded CAD
180 lines (159 loc) • 5.96 kB
JavaScript
import * as Error from './error';
import Events from './events';
function createStates(dom, events, listener, options = undefined) {
return events.reduce(
(prev, name) => {
dom.addEventListener(name, listener, options);
return {
...prev,
[name]: {
type: name,
fulfilled: false,
detach: () => dom.removeEventListener(name, listener)
}
};
},
{}
);
}
export default class Dom {
constructor(view = Error.log('View undefined'), handlers = {}) {
this.view = view;
this.canvas = view.canvas;
this.renderer = view.renderer;
this.dom = view.dom;
this.recent = [];
this.enabled = true;
this.debounce = true;
// Mix in event generic handler
Events.mixin(this, handlers);
// Add internal dom event handlers
this.eventHandler = this.handleEvent.bind(this);
this.eventHandlerDefault = this.handleEventDefault.bind(this);
let passiveStates = createStates(
this.dom,
['mousedown', 'mouseup', 'mousemove', 'mouseout'],
this.eventHandler,
{ passive: true }
);
let standardStates = createStates(
this.dom,
['touchstart', 'touchmove', 'touchend', 'wheel'],
this.eventHandlerDefault,
{ passive: false }
);
let globalStates = createStates(
document,
['keydown', 'keyup'],
this.eventHandler,
{ passive: true }
);
this.states = { ...passiveStates, ...standardStates, ...globalStates };
// Common last-state-data can be accessed here
this.mouse = {};
this.wheel = {};
this.touch = {};
this.keys = {};
// Array of changes to be called on next update
this.changes = [];
}
// This seems to be buggy, doesn't detach mouse event handlers for some weird reason
detach() {
for (let key in this.states)
this.states[key].detach();
}
update() {
if (this.changes.length > 0) {
for (let change of this.changes) {
this.emit(change.type, change);
change.fulfilled = false;
}
this.changes = [];
}
}
handleEventDefault(event) {
event.preventDefault();
this.handleEvent(event);
}
handleEvent(event) {
if (!this.enabled) return;
let { type, pageX, pageY, clientX, clientY, button, which, deltaMode, deltaY } = event;
let state = this.states[type];
if (this.debounce && state.fulfilled) return;
// Wheel
state.delta = deltaY * (deltaMode ? -1 : -0.03);
// Generic
state.fulfilled = true;
state.pageX = pageX;
state.pageY = pageY;
state.clientX = clientX;
state.clientY = clientY;
state.offsetX = pageX - this.renderer.offset.left - this.view.left;
state.offsetY = pageY - this.renderer.offset.top - this.view.top;
// Touch
state.touches = [];
if (!!event.touches) {
for (let item of [].slice.call(event.touches)) {
pageX = item.pageX;
pageY = item.pageY;
clientX = item.clientX;
clientY = item.clientY;
state.touches.push({
clientX: clientX,
clientY: clientY,
offsetX: pageX - this.renderer.offset.left - this.view.left,
offsetY: pageY - this.renderer.offset.top - this.view.top,
pageX: pageX,
pageY: pageY
});
}
state.touch = true;
state.multitouch = state.touches.length > 1;
if (state.touches.length > 0) {
state.offsetX = this.touch.offsetX = state.touches[0].offsetX;
state.offsetY = this.touch.offsetX = state.touches[0].offsetY;
} else {
state.offsetX = this.touch.offsetX;
state.offsetY = this.touch.offsetX;
}
}
state.button = button;
state.which = which;
state.event = event;
this.changes.push(state);
const keyToName = {
16: "shift",
17: "control",
18: "alt",
};
if (type === 'mousedown') {
this.mouse.down = true;
this.mouse.button = button;
document.addEventListener('mouseup', this.eventHandlerDefault, false);
document.addEventListener('mousemove', this.eventHandlerDefault, false);
} else if (type === 'mouseup') {
this.mouse.down = false;
this.mouse.button = button;
document.removeEventListener('mouseup', this.eventHandlerDefault);
document.removeEventListener('mousemove', this.eventHandlerDefault);
} else if (type === 'touchstart') {
this.touch.down = true;
document.addEventListener('touchend', this.eventHandlerDefault, false);
document.addEventListener('touchmove', this.eventHandlerDefault, false);
} else if (type === 'touchend') {
this.touch.down = false;
document.removeEventListener('touchend', this.eventHandlerDefault);
document.removeEventListener('touchmove', this.eventHandlerDefault);
} else if (type === 'keydown') {
let name = keyToName[which];
if (name)
this.keys[name] = true;
} else if (type === 'keyup') {
let name = keyToName[which];
if (name)
this.keys[name] = false;
}
this.recent[state.type] = state;
if (!this.debounce) this.update();
}
}