UNPKG

meta-next

Version:

499 lines (409 loc) 11.1 kB
import Device from "./Device" import Engine from "./Engine" import Key from "./Key" const numKeys = 256 const numInputs = 10 const numTotalKeys = numKeys + numInputs + 1 function Subscriber(owner, func) { this.owner = owner this.func = func } class Input { constructor() { this.listeners = {} this.ignoreKeys = {} this.cmdKeys = {} this.iframeKeys = {} this.enable = true this.stickyKeys = false this.metaPressed = false this.firstInputEvent = true this.inputs = new Array(numTotalKeys) this.touches = [] Device.on("visible", (value) => { if(!value) { this.reset() } }) this.x = 0 this.y = 0 this.screenX = 0 this.screenY = 0 this.prevX = 0 this.prevY = 0 this.prevScreenX = 0 this.prevScreenY = 0 loadIgnoreKeys(this) addEventListeners(this) } handleKeyDown(domEvent) { this.checkIgnoreKey(domEvent) if(!this.enable) { return } const keyCode = domEvent.keyCode if(this.stickyKeys && this.inputs[keyCode]) { return } if(domEvent.keyIdentifier === "Meta") { this.metaPressed = true } else if(this.metaPressed) { return } this.inputs[keyCode] = 1 const inputEvent = new Input.Event() inputEvent.domEvent = domEvent inputEvent.keyCode = keyCode this.emit("keydown", inputEvent) } handleKeyUp(domEvent) { this.checkIgnoreKey(domEvent) if(!this.enable) { return } const keyCode = domEvent.keyCode this.metaPressed = false this.inputs[keyCode] = 0 const inputEvent = new Input.Event() inputEvent.domEvent = domEvent inputEvent.keyCode = keyCode this.emit("keyup", inputEvent) } handleMouseDown(domEvent) { this.handleMouseEvent("down", domEvent) } handleMouseUp(domEvent) { this.handleMouseEvent("up", domEvent) } handleMouseDblClick(domEvent) { this.handleMouseEvent("dblclick", domEvent) } handleMouseMove(domEvent) { if(document.activeElement === document.body) { domEvent.preventDefault() } this.handleMouseEvent("move", domEvent) } handleMouseWheel(domEvent) { if(document.activeElement === document.body) { domEvent.preventDefault() } this.handleMouseEvent("wheel", domEvent) } handleMouseEvent(eventType, domEvent) { if(!Engine.gl || domEvent.target !== Engine.gl.canvas) { return } if(!this.enable) { return } const wnd = Engine.window const camera = Engine.camera this.prevScreenX = this.screenX this.prevScreenY = this.screenY this.screenX = ((domEvent.pageX - wnd.offsetLeft) * wnd.scaleX) / wnd.ratio this.screenY = ((domEvent.pageY - wnd.offsetTop) * wnd.scaleY) / wnd.ratio this.prevX = this.x this.prevY = this.y this.x = ((this.screenX * camera._scale.x) - camera.x) / wnd.ratio | 0 this.y = ((this.screenY * camera._scale.y) - camera.y) / wnd.ratio | 0 const inputEvent = new Input.Event() inputEvent.domEvent = domEvent inputEvent.screenX = this.screenX inputEvent.screenY = this.screenY inputEvent.x = this.x inputEvent.y = this.y switch(eventType) { case "down": case "dblclick": case "up": { const keyCode = domEvent.button + Key.BUTTON_ENUM_OFFSET this.inputs[keyCode] = (eventType === "up") ? 0 : 1 if(this.firstInputEvent) { inputEvent.deltaX = 0 inputEvent.deltaY = 0 this.firstInputEvent = false } else { inputEvent.deltaX = (this.prevScreenX - this.screenX) * camera.zoomRatio / wnd.ratio inputEvent.deltaY = (this.prevScreenY - this.screenY) * camera.zoomRatio / wnd.ratio } inputEvent.keyCode = keyCode } break case "move": { if(this.firstInputEvent) { inputEvent.deltaX = 0 inputEvent.deltaY = 0 this.firstInputEvent = false } else { inputEvent.deltaX = -domEvent.movementX * camera.zoomRatio / wnd.ratio inputEvent.deltaY = -domEvent.movementY * camera.zoomRatio / wnd.ratio } inputEvent.keyCode = 0 } break case "wheel": { inputEvent.deltaX = Math.max(-1, Math.min(1, (domEvent.wheelDelta || -domEvent.detail))) inputEvent.deltaY = inputEvent.deltaX inputEvent.keyCode = 0 } break } this.emit(eventType, inputEvent) } handleTouchDown(domEvent) { this.handleTouchEvent(domEvent, "down") } handleTouchUp(domEvent) { this.handleTouchEvent(domEvent, "up") } handleTouchMove(domEvent) { this.handleTouchEvent(domEvent, "move") } handleTouchEvent(domEvent, eventType) { if(!Engine.gl || domEvent.target !== Engine.gl.canvas) { return } // if(document.activeElement === document.body) { // domEvent.preventDefault() // } const wnd = Engine.window const camera = Engine.camera const changedTouches = domEvent.changedTouches for(let n = 0; n < changedTouches.length; n++) { const touch = changedTouches[n] let id switch(eventType) { case "down": id = this.touches.length this.touches.push(touch.identifier) break case "up": id = this.getTouchID(touch.identifier) if(id === -1) { continue } this.touches.splice(id, 1) break case "move": id = this.getTouchID(touch.identifier) break } const screenX = ((touch.pageX - wnd.offsetLeft) * wnd.scaleX) const screenY = ((touch.pageY - wnd.offsetTop) * wnd.scaleY) const x = (screenX * camera.zoomRatio) - camera.x | 0 const y = (screenY * camera.zoomRatio) - camera.y | 0 const keyCode = id + Key.BUTTON_ENUM_OFFSET const inputEvent = new Input.Event() inputEvent.domEvent = domEvent inputEvent.screenX = screenX inputEvent.screenY = screenY inputEvent.x = x inputEvent.y = y inputEvent.keyCode = keyCode if(id === 0) { this.prevX = this.x this.prevY = this.y this.prevScreenX = this.screenX this.prevScreenY = this.screenY this.x = x this.y = y this.screenX = screenX this.screenY = screenY if(this.firstInputEvent) { inputEvent.deltaX = 0 inputEvent.deltaY = 0 this.firstInputEvent = false } else { inputEvent.deltaX = this.prevScreenX - this.screenX inputEvent.deltaY = this.prevScreenY - this.screenY } } switch(eventType) { case "down": this.inputs[keyCode] = 1 break case "up": this.inputs[keyCode] = 0 break } this.emit(eventType, inputEvent) } } pressed(keyCode) { return this.inputs[keyCode] } on(event, func, owner) { const sub = new Subscriber(owner, func) let buffer = this.listeners[event] if(buffer) { buffer.push(sub) } else { buffer = [ sub ] this.listeners[event] = buffer } } off(event, func, owner) { const buffer = this.listeners[event] if(!buffer) { return } for(let n = 0; n < buffer.length; n++) { const sub = buffer[n] if(sub.func === func && sub.owner === owner) { buffer[n] = buffer[buffer.length - 1] buffer.pop() return } } } emit(event, arg) { const buffer = this.listeners[event] if(!buffer) { return } for(let n = 0; n < buffer.length; n++) { const sub = buffer[n] sub.func.call(sub.owner, arg) } } reset() { // Reset keys: for(let n = 0; n < numKeys.length; n++) { if(!this.inputs[n]) { continue } this.inputs[n] = 0 const inputEvent = new Input.Event() inputEvent.domEvent = domEvent inputEvent.keyCode = keyCode this.emit("keyup", inputEvent) } // Reset inputs: for(let n = numKeys; n <= numKeys + numTouches; n++) { const keyCode = n + Key.BUTTON_ENUM_OFFSET if(!this.inputs[keyCode]) { continue } const inputEvent = new Input.Event() inputEvent.domEvent = domEvent inputEvent.keyCode = keyCode this.emit("up", inputEvent) } // Reset touches: for(let n = 0; n < this.touches.length; n++) { if(!this.touches[n]) { continue } const keyCode = n + Key.BUTTON_ENUM_OFFSET if(!this.inputs[keyCode]) { continue } const inputEvent = new Input.Event() inputEvent.domEvent = domEvent inputEvent.keyCode = keyCode this.emit("up", inputEvent) } } getTouchID(eventTouchID) { for(let n = 0; n < this.touches.length; n++) { if(this.touches[n] === eventTouchID) { return n } } return -1 } checkIgnoreKey(domEvent) { const keyCode = domEvent.keyCode if(document.activeElement === document.body) { if(window.top && this.iframeKeys[keyCode]) { domEvent.preventDefault() } if(this.cmdKeys[keyCode] !== undefined) { this.numCmdKeysPressed++ } if(this.ignoreKeys[keyCode] !== undefined && this.numCmdKeysPressed <= 0) { domEvent.preventDefault() } } } set ignoreFKeys(flag) { if(flag) { ignoreFKeys(this, 1) } else { ignoreFKeys(this, 0) } } get ignoreFKeys() { return !!this.ignoreKeys[112] } } const addEventListeners = (input) => { window.addEventListener("mousedown", input.handleMouseDown.bind(input)) window.addEventListener("mouseup", input.handleMouseUp.bind(input)) window.addEventListener("mousemove", input.handleMouseMove.bind(input)) window.addEventListener("mousewheel", input.handleMouseWheel.bind(input)) window.addEventListener("dblclick", input.handleMouseDblClick.bind(input)) window.addEventListener("touchstart", input.handleTouchDown.bind(input)) window.addEventListener("touchend", input.handleTouchUp.bind(input)) window.addEventListener("touchmove", input.handleTouchMove.bind(input)) window.addEventListener("touchcancel", input.handleTouchUp.bind(input)) window.addEventListener("touchleave", input.handleTouchUp.bind(input)) if(Device.supports.onkeydown) { window.addEventListener("keydown", input.handleKeyDown.bind(input)) } if(Device.supports.onkeyup) { window.addEventListener("keyup", input.handleKeyUp.bind(input)) } } const loadIgnoreKeys = (input) => { input.ignoreKeys = {} input.ignoreKeys[8] = 1 input.ignoreKeys[9] = 1 input.ignoreKeys[13] = 1 input.ignoreKeys[17] = 1 input.ignoreKeys[91] = 1 input.ignoreKeys[38] = 1 input.ignoreKeys[39] = 1 input.ignoreKeys[40] = 1 input.ignoreKeys[37] = 1 input.ignoreKeys[124] = 1 input.ignoreKeys[125] = 1 input.ignoreKeys[126] = 1 input.cmdKeys[91] = 1 input.cmdKeys[17] = 1 input.iframeKeys[37] = 1 input.iframeKeys[38] = 1 input.iframeKeys[39] = 1 input.iframeKeys[40] = 1 } const ignoreFKeys = (input, value) => { for(let n = 112; n <= 123; n++) { input.ignoreKeys[n] = value } } Input.Event = function() { this.domEvent = null this.x = 0 this.y = 0 this.deltaX = 0 this.deltaY = 0 this.screenX = 0 this.screenY = 0 this.keyCode = 0 this.entity = null } const instance = new Input export default instance