import {EventManager} from "../utils/EventManager.js"; import {Vector2} from "../math/Vector2.js"; import {Key} from "./Key.js"; /** * Pointer object is used to called input from the user, works for booth mouse or touch screens. * * It is responsible for synchronizing user input with the render of the graphics. * * @class * @param {Element} domElement DOM element to create the pointer events. * @param {Element} canvas Canvas DOM element where the content is being drawn. */ function Pointer(domElement, canvas) { //Raw data this._keys = new Array(5); this._position = new Vector2(0, 0); this._positionUpdated = false; this._delta = new Vector2(0, 0); this._wheel = 0; this._wheelUpdated = false; this._doubleClicked = new Array(5); /** * Array with pointer buttons status. * * @type {number[]} */ this.keys = new Array(5); /** * Pointer position inside of the window (coordinates in window space). * * This value is accumulated from multiple mouse triggered events between updated. * * @type {Vector2} */ this.position = new Vector2(0, 0); /** * Pointer movement (coordinates in window space). Since the last update. * * This value is accumulated from multiple mouse triggered events between updated. * * @type {Vector2} */ this.delta = new Vector2(0, 0); /** * Pointer scroll wheel movement, since the last update. * * @type {number} */ this.wheel = 0; /** * Indicates a button of the pointer was double clicked. * * @type {boolean} */ this.doubleClicked = new Array(5); /** * DOM element where to attach the pointer events. * * @type {Element} */ this.domElement = (domElement !== undefined) ? domElement : window; /** * Canvas attached to this pointer instance used to calculate position and delta in element space coordinates. * * @type {Element} */ this.canvas = null; if(canvas !== undefined) { this.setCanvas(canvas); } /** * Event manager responsible for updating the raw data variables. * * Different events are used depending on the host platform. * * When the update method is called the raw data is reset. * * @type {EventManager} */ this.events = new EventManager(); //Initialize key instances for(var i = 0; i < 5; i++) { this._doubleClicked[i] = false; this.doubleClicked[i] = false; this._keys[i] = new Key(); this.keys[i] = new Key(); } //Self pointer var self = this; //Scroll wheel if(window.onmousewheel !== undefined) { //Chrome, edge this.events.add(this.domElement, "mousewheel", function(event) { self._wheel = event.deltaY; self._wheelUpdated = true; }); } else if(window.addEventListener !== undefined) { //Firefox this.events.add(this.domElement, "DOMMouseScroll", function(event) { self._wheel = event.detail * 30; self._wheelUpdated = true; }); } else { this.events.add(this.domElement, "wheel", function(event) { self._wheel = event.deltaY; self._wheelUpdated = true; }); } //Touchscreen input events if(window.ontouchstart !== undefined || navigator.msMaxTouchPoints > 0) { //Auxiliar variables to calculate touch delta var lastTouch = new Vector2(0, 0); //Touch start event this.events.add(this.domElement, "touchstart", function(event) { var touch = event.touches[0]; self.updatePosition(touch.clientX, touch.clientY, 0, 0); self.updateKey(Pointer.LEFT, Key.DOWN); lastTouch.set(touch.clientX, touch.clientY); }); //Touch end event this.events.add(this.domElement, "touchend", function(event) { self.updateKey(Pointer.LEFT, Key.UP); }); //Touch cancel event this.events.add(this.domElement, "touchcancel", function(event) { self.updateKey(Pointer.LEFT, Key.UP); }); //Touch move event this.events.add(document.body, "touchmove", function(event) { var touch = event.touches[0]; self.updatePosition(touch.clientX, touch.clientY, touch.clientX - lastTouch.x, touch.clientY - lastTouch.y); lastTouch.set(touch.clientX, touch.clientY); }); } //Move this.events.add(this.domElement, "mousemove", function(event) { self.updatePosition(event.clientX, event.clientY, event.movementX, event.movementY); }); //Button pressed this.events.add(this.domElement, "mousedown", function(event) { self.updateKey(event.which - 1, Key.DOWN); }); //Button released this.events.add(this.domElement, "mouseup", function(event) { self.updateKey(event.which - 1, Key.UP); }); //Drag start this.events.add(this.domElement, "dragstart", function(event) { self.updateKey(event.which - 1, Key.UP); }); //Pointer double click this.events.add(this.domElement, "dblclick", function(event) { self._doubleClicked[event.which - 1] = true; }); this.create(); } Pointer.prototype = Pointer; Pointer.prototype.constructor = Pointer; /** * Left pointer button. * * @static * @type {number} */ Pointer.LEFT = 0; /** * Middle pointer button. * * @static * @type {number} */ Pointer.MIDDLE = 1; /** * Right pointer button. * * @static * @type {number} */ Pointer.RIGHT = 2; /** * Back pointer navigation button. * * @static * @type {number} */ Pointer.BACK = 3; /** * Forward pointer navigation button. * * @static * @type {number} */ Pointer.FORWARD = 4; /** * Element to be used for coordinates calculation relative to that canvas. * * @param {DOM} element Canvas to be attached to the Pointer instance */ Pointer.setCanvas = function(element) { this.canvas = element; element.pointerInside = false; element.addEventListener("mouseenter", function() { this.pointerInside = true; }); element.addEventListener("mouseleave", function() { this.pointerInside = false; }); }; /** * Check if pointer is inside attached canvas (updated async). * * @return {boolean} True if pointer is currently inside the canvas */ Pointer.insideCanvas = function() { return this.canvas !== null && this.canvas.pointerInside; }; /** * Check if pointer button is currently pressed. * * @param {Number} button Button to check status of * @return {boolean} True if button is currently pressed */ Pointer.buttonPressed = function(button) { return this.keys[button].pressed; }; /** * Check if pointer button was double clicked. * * @param {Number} button Button to check status of * @return {boolean} True if some pointer button was just double clicked */ Pointer.buttonDoubleClicked = function(button) { return this.doubleClicked[button]; }; /** * Check if a pointer button was just pressed. * * @param {Number} button Button to check status of * @return {boolean} True if button was just pressed */ Pointer.buttonJustPressed = function(button) { return this.keys[button].justPressed; }; /** * Check if a pointer button was just released. * * @param {Number} button Button to check status of * @return {boolean} True if button was just released */ Pointer.buttonJustReleased = function(button) { return this.keys[button].justReleased; }; /** * Update pointer position. * * @param {Number} x * @param {Number} y * @param {Number} xDiff * @param {Number} yDiff */ Pointer.updatePosition = function(x, y, xDiff, yDiff) { if(this.canvas !== null) { var rect = this.canvas.getBoundingClientRect(); x -= rect.left; y -= rect.top; } this._position.set(x, y); this._delta.x += xDiff; this._delta.y += yDiff; this._positionUpdated = true; }; /** * Update a pointer button. * * @param {Number} button * @param {Number} action */ Pointer.updateKey = function(button, action) { if(button > -1) { this._keys[button].update(action); } }; /** * Update pointer buttons state, position, wheel and delta synchronously. * * Should be called every frame on the update loop before reading any values from the pointer. */ Pointer.update = function() { //Update pointer keys state for(var i = 0; i < 5; i++) { if(this._keys[i].justPressed && this.keys[i].justPressed) { this._keys[i].justPressed = false; } if(this._keys[i].justReleased && this.keys[i].justReleased) { this._keys[i].justReleased = false; } this.keys[i].set(this._keys[i].justPressed, this._keys[i].pressed, this._keys[i].justReleased); //Update pointer double click if(this._doubleClicked[i] === true) { this.doubleClicked[i] = true; this._doubleClicked[i] = false; } else { this.doubleClicked[i] = false; } } //Update pointer wheel if(this._wheelUpdated) { this.wheel = this._wheel; this._wheelUpdated = false; } else { this.wheel = 0; } //Update pointer Position if needed if(this._positionUpdated) { this.delta.copy(this._delta); this.position.copy(this._position); this._delta.set(0,0); this._positionUpdated = false; } else { this.delta.x = 0; this.delta.y = 0; } }; /** * Create pointer events to collect input data. * * Should be called before using the pointer object. */ Pointer.create = function() { this.events.create(); }; /** * Dispose pointer events, should be called after the objects is no longer required. * * If not called leaves the window events created leaving a memory/code leak. */ Pointer.dispose = function() { this.events.destroy(); }; export {Pointer};