UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

300 lines (299 loc) 9.54 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { platform } from "../../core/platform.js"; import { EventHandler } from "../../core/event-handler.js"; import { isMousePointerLocked, MouseEvent } from "./mouse-event.js"; class Mouse extends EventHandler { /** * Create a new Mouse instance. * * @param {Element} [element] - The Element that the mouse events are attached to. */ constructor(element) { super(); /** @private */ __publicField(this, "_lastX", 0); /** @private */ __publicField(this, "_lastY", 0); /** @private */ __publicField(this, "_buttons", [false, false, false]); /** @private */ __publicField(this, "_lastbuttons", [false, false, false]); /** @private */ __publicField(this, "_target", null); /** @private */ __publicField(this, "_attached", false); /** * @type {(event: globalThis.MouseEvent) => void} * @private */ __publicField(this, "_upHandler"); /** * @type {(event: globalThis.MouseEvent) => void} * @private */ __publicField(this, "_downHandler"); /** * @type {(event: globalThis.MouseEvent) => void} * @private */ __publicField(this, "_moveHandler"); /** * @type {(event: globalThis.WheelEvent) => void} * @private */ __publicField(this, "_wheelHandler"); /** * @type {(event: Event) => void} * @private */ __publicField(this, "_contextMenuHandler"); this._upHandler = this._handleUp.bind(this); this._downHandler = this._handleDown.bind(this); this._moveHandler = this._handleMove.bind(this); this._wheelHandler = this._handleWheel.bind(this); this._contextMenuHandler = (event) => { event.preventDefault(); }; this.attach(element); } /** * Check if the mouse pointer has been locked, using {@link enablePointerLock}. * * @returns {boolean} True if locked. */ static isPointerLocked() { return isMousePointerLocked(); } /** * Attach mouse events to an Element. * * @param {Element} element - The DOM element to attach the mouse to. */ attach(element) { this._target = element; if (this._attached) return; this._attached = true; const passiveOptions = { passive: false }; const options = platform.passiveEvents ? passiveOptions : false; window.addEventListener("mouseup", this._upHandler, options); window.addEventListener("mousedown", this._downHandler, options); window.addEventListener("mousemove", this._moveHandler, options); window.addEventListener("wheel", this._wheelHandler, options); } /** * Remove mouse events from the element that it is attached to. */ detach() { if (!this._attached) return; this._attached = false; this._target = null; const passiveOptions = { passive: false }; const options = platform.passiveEvents ? passiveOptions : false; window.removeEventListener("mouseup", this._upHandler, options); window.removeEventListener("mousedown", this._downHandler, options); window.removeEventListener("mousemove", this._moveHandler, options); window.removeEventListener("wheel", this._wheelHandler, options); } /** * Disable the context menu usually activated with right-click. */ disableContextMenu() { if (!this._target) return; this._target.addEventListener("contextmenu", this._contextMenuHandler); } /** * Enable the context menu usually activated with right-click. This option is active by * default. */ enableContextMenu() { if (!this._target) return; this._target.removeEventListener("contextmenu", this._contextMenuHandler); } /** * Request that the browser hides the mouse cursor and locks the mouse to the element. Allowing * raw access to mouse movement input without risking the mouse exiting the element. Notes: * * - In some browsers this will only work when the browser is running in fullscreen mode. See * {@link https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API Fullscreen API} for * more details. * - Enabling pointer lock can only be initiated by a user action e.g. in the event handler for * a mouse or keyboard input. * * @param {LockMouseCallback} [success] - Function called if the request for mouse lock is * successful. * @param {LockMouseCallback} [error] - Function called if the request for mouse lock is * unsuccessful. */ enablePointerLock(success, error) { if (!document.body.requestPointerLock) { if (error) { error(); } return; } const s = () => { success(); document.removeEventListener("pointerlockchange", s); }; const e = () => { error(); document.removeEventListener("pointerlockerror", e); }; if (success) { document.addEventListener("pointerlockchange", s, false); } if (error) { document.addEventListener("pointerlockerror", e, false); } document.body.requestPointerLock(); } /** * Return control of the mouse cursor to the user. * * @param {LockMouseCallback} [success] - Function called when the mouse lock is disabled. */ disablePointerLock(success) { if (!document.exitPointerLock) { return; } const s = () => { success(); document.removeEventListener("pointerlockchange", s); }; if (success) { document.addEventListener("pointerlockchange", s, false); } document.exitPointerLock(); } /** * Update method, should be called once per frame. */ update() { this._lastbuttons[0] = this._buttons[0]; this._lastbuttons[1] = this._buttons[1]; this._lastbuttons[2] = this._buttons[2]; } /** * Returns true if the mouse button is currently pressed. * * @param {number} button - The mouse button to test. Can be: * * - {@link MOUSEBUTTON_LEFT} * - {@link MOUSEBUTTON_MIDDLE} * - {@link MOUSEBUTTON_RIGHT} * * @returns {boolean} True if the mouse button is current pressed. */ isPressed(button) { return this._buttons[button]; } /** * Returns true if the mouse button was pressed this frame (since the last call to update). * * @param {number} button - The mouse button to test. Can be: * * - {@link MOUSEBUTTON_LEFT} * - {@link MOUSEBUTTON_MIDDLE} * - {@link MOUSEBUTTON_RIGHT} * * @returns {boolean} True if the mouse button was pressed since the last update. */ wasPressed(button) { return this._buttons[button] && !this._lastbuttons[button]; } /** * Returns true if the mouse button was released this frame (since the last call to update). * * @param {number} button - The mouse button to test. Can be: * * - {@link MOUSEBUTTON_LEFT} * - {@link MOUSEBUTTON_MIDDLE} * - {@link MOUSEBUTTON_RIGHT} * * @returns {boolean} True if the mouse button was released since the last update. */ wasReleased(button) { return !this._buttons[button] && this._lastbuttons[button]; } _handleUp(event) { this._buttons[event.button] = false; const e = new MouseEvent(this, event); if (!e.event) return; this.fire("mouseup", e); } _handleDown(event) { this._buttons[event.button] = true; const e = new MouseEvent(this, event); if (!e.event) return; this.fire("mousedown", e); } _handleMove(event) { const e = new MouseEvent(this, event); if (!e.event) return; this.fire("mousemove", e); this._lastX = e.x; this._lastY = e.y; } _handleWheel(event) { const e = new MouseEvent(this, event); if (!e.event) return; this.fire("mousewheel", e); } _getTargetCoords(event) { const rect = this._target.getBoundingClientRect(); const left = Math.floor(rect.left); const top = Math.floor(rect.top); if (event.clientX < left || event.clientX >= left + this._target.clientWidth || event.clientY < top || event.clientY >= top + this._target.clientHeight) { return null; } return { x: event.clientX - left, y: event.clientY - top }; } } /** * Fired when the mouse is moved. The handler is passed a {@link MouseEvent}. * * @event * @example * app.mouse.on('mousemove', (e) => { * console.log(`Current mouse position is: ${e.x}, ${e.y}`); * }); */ __publicField(Mouse, "EVENT_MOUSEMOVE", "mousemove"); /** * Fired when a mouse button is pressed. The handler is passed a {@link MouseEvent}. * * @event * @example * app.mouse.on('mousedown', (e) => { * console.log(`The ${e.button} button was pressed at position: ${e.x}, ${e.y}`); * }); */ __publicField(Mouse, "EVENT_MOUSEDOWN", "mousedown"); /** * Fired when a mouse button is released. The handler is passed a {@link MouseEvent}. * * @event * @example * app.mouse.on('mouseup', (e) => { * console.log(`The ${e.button} button was released at position: ${e.x}, ${e.y}`); * }); */ __publicField(Mouse, "EVENT_MOUSEUP", "mouseup"); /** * Fired when a mouse wheel is moved. The handler is passed a {@link MouseEvent}. * * @event * @example * app.mouse.on('mousewheel', (e) => { * console.log(`The mouse wheel was moved by ${e.wheelDelta}`); * }); */ __publicField(Mouse, "EVENT_MOUSEWHEEL", "mousewheel"); export { Mouse };