UNPKG

oxygen-core

Version:

Oxygen game engine (Xenon Core for browsers)

290 lines (258 loc) 7.86 kB
import System from './System'; import Events from '../utils/Events'; import { vec2 } from '../utils/gl-matrix'; const cachedUnitsVector = vec2.create(); const cachedScreenVector = vec2.create(); const touchDetectionMediaQuery = [ '(-webkit-heartz)', '(-moz-heartz)', '(-o-heartz)', '(-ms-heartz)', '(heartz)' ].join(','); function detectTouchDevice() { if ( ('ontouchstart' in window) || (!!window.DocumentTouch && document instanceof DocumentTouch) ) { return true; } else { return window.matchMedia(touchDetectionMediaQuery).matches; } } /** * User input (mouse, keyboard, gamepad). * * @example * const system = new InputSystem(document.getElementById('screen-0')); */ export default class InputSystem extends System { /** @type {Events} */ get events() { return this._events; } /** @type {Map} */ get gamepads() { return this._gamepads; } /** @type {boolean}*/ get isTouchDevice() { return this._isTouchDevice; } /** @type {boolean} */ get triggerEvents() { return this._triggerEvents; } /** * Constructor. * * @param {HTMLCanvasElement} canvas - Canvas element to listen for events from. * @param {boolean} triggerEvents - Tells if system should trigger events. */ constructor(canvas, triggerEvents = true) { super(); this._canvas = canvas; this._events = new Events(); this._gamepads = new Map(); this._triggerEvents = !!triggerEvents; this._onMouseDown = this.onMouseDown.bind(this); this._onMouseUp = this.onMouseUp.bind(this); this._onMouseMove = this.onMouseMove.bind(this); this._onTouchDown = this.onTouchDown.bind(this); this._onTouchUp = this.onTouchUp.bind(this); this._onTouchMove = this.onTouchMove.bind(this); this._onKeyDown = this.onKeyDown.bind(this); this._onKeyUp = this.onKeyUp.bind(this); this._isTouchDevice = detectTouchDevice(); } dispose() { super.dispose(); if (!!this._events) { this._events.dispose(); } this._canvas = null; this._events = null; this._gamepads = null; this._onMouseDown = null; this._onMouseUp = null; this._onMouseMove = null; this._onTouchDown = null; this._onTouchUp = null; this._onTouchMove = null; this._onKeyDown = null; this._onKeyUp = null; } /** * @override */ onRegister() { this._canvas.addEventListener('mousedown', this._onMouseDown); document.addEventListener('mouseup', this._onMouseUp); document.addEventListener('mousemove', this._onMouseMove); this._canvas.addEventListener('touchstart', this._onTouchDown); this._canvas.addEventListener('touchend', this._onTouchUp); this._canvas.addEventListener('touchcancel', this._onTouchUp); this._canvas.addEventListener('touchmove', this._onTouchMove); document.addEventListener('keydown', this._onKeyDown); document.addEventListener('keyup', this._onKeyUp); } /** * @override */ onUnregister() { this._canvas.removeEventListener('mousedown', this._onMouseDown); document.removeEventListener('mouseup', this._onMouseUp); document.removeEventListener('mousemove', this._onMouseMove); this._canvas.removeEventListener('touchstart', this._onTouchDown); this._canvas.removeEventListener('touchend', this._onTouchUp); this._canvas.removeEventListener('touchcancel', this._onTouchUp); this._canvas.removeEventListener('touchmove', this._onTouchMove); document.removeEventListener('keydown', this._onKeyDown); document.removeEventListener('keyup', this._onKeyUp); } onMouseDown(event, target) { this._canvasToUnitCoords( cachedUnitsVector, cachedScreenVector, event, target ); !!this._triggerEvents && !!this._events && this._events.trigger( 'mouse-down', cachedUnitsVector, cachedScreenVector, event.button ); } onMouseUp(event, target) { this._canvasToUnitCoords( cachedUnitsVector, cachedScreenVector, event, target ); !!this._triggerEvents && !!this._events && this._events.trigger( 'mouse-up', cachedUnitsVector, cachedScreenVector, event.button ); } onMouseMove(event, target) { this._canvasToUnitCoords( cachedUnitsVector, cachedScreenVector, event, target ); !!this._triggerEvents && !!this._events && this._events.trigger( 'mouse-move', cachedUnitsVector, cachedScreenVector ); } onTouchDown(event, target) { for (const touch of event.changedTouches) { this._canvasToUnitCoords( cachedUnitsVector, cachedScreenVector, touch, target ); !!this._triggerEvents && !!this._events && this._events.trigger( 'touch-down', cachedUnitsVector, cachedScreenVector, touch.identifier ); } } onTouchUp(event, target) { for (const touch of event.changedTouches) { this._canvasToUnitCoords( cachedUnitsVector, cachedScreenVector, touch, target ); !!this._triggerEvents && !!this._events && this._events.trigger( 'touch-up', cachedUnitsVector, cachedScreenVector, touch.identifier ); } } onTouchMove(event, target) { for (const touch of event.changedTouches) { this._canvasToUnitCoords( cachedUnitsVector, cachedScreenVector, touch, target ); !!this._triggerEvents && !!this._events && this._events.trigger( 'touch-move', cachedUnitsVector, cachedScreenVector, touch.identifier ); } } onKeyDown(event) { !!this._triggerEvents && !!this._events && this._events.trigger( 'key-down', event.which || event.keyCode ); } onKeyUp(event) { !!this._triggerEvents && !!this._events && this._events.trigger( 'key-up', event.which || event.keyCode ); } /** * Scan for changes in browser gamepads list. */ scanForGamepads() { const { _gamepads, _events, _triggerEvents } = this; const gamepads = !!navigator.getGamepads ? [ ...navigator.getGamepads() ] : (!!navigator.webkitGetGamepads ? [ ...navigator.webkitGetGamepads() ] : null); if (!gamepads) { return; } for (let i = 0, c = gamepads.length; i < c; ++i) { const gamepad = gamepads[i]; if (!gamepad) { continue; } const { id } = gamepad; if (!_gamepads.has(id)) { _gamepads.set(id, gamepad); !!_triggerEvents && !!_events && _events.trigger('gamepad-connected', gamepad); } } for (const gamepad of _gamepads.values()) { if (!gamepad.connected || gamepads.indexOf(gamepad) < 0) { _gamepads.delete(gamepad.id); !!_triggerEvents && !!_events && _events.trigger('gamepad-disconnected', gamepad); } else { !!_triggerEvents && !!_events && _events.trigger('gamepad-process', gamepad); } } } _canvasToUnitCoords(outUnits, outScreen, event, target) { target = target || event.target; const { width, height } = this._canvas; const bounds = target.getBoundingClientRect(); let x = event.clientX - bounds.left; let y = event.clientY - bounds.top; outScreen[0] = x = x * target.width / target.clientWidth; outScreen[1] = y = y * target.height / target.clientHeight; outUnits[0] = x / width * 2 - 1; outUnits[1] = y / height * -2 + 1; } }