Files
cocos-engine/pal/input/web/mouse-input.ts

148 lines
5.7 KiB
TypeScript

import { TEST } from 'internal:constants';
import { MouseCallback } from 'pal/input';
import { systemInfo } from 'pal/system-info';
import { screenAdapter } from 'pal/screen-adapter';
import { EventMouse } from '../../../cocos/input/types';
import { EventTarget } from '../../../cocos/core/event';
import { Rect, Vec2 } from '../../../cocos/core/math';
import { InputEventType } from '../../../cocos/input/types/event-enum';
import { Feature } from '../../system-info/enum-type';
export class MouseInputSource {
private _canvas?: HTMLCanvasElement;
private _eventTarget: EventTarget = new EventTarget();
private _pointLocked = false;
private _isPressed = false;
private _preMousePos: Vec2 = new Vec2();
constructor () {
if (systemInfo.hasFeature(Feature.EVENT_MOUSE)) {
this._canvas = document.getElementById('GameCanvas') as HTMLCanvasElement;
if (!this._canvas && !TEST) {
console.warn('failed to access canvas');
}
this._registerEvent();
}
}
private _getCanvasRect (): Rect {
const canvas = this._canvas;
const box = canvas?.getBoundingClientRect();
if (box) {
return new Rect(box.x, box.y, box.width, box.height);
}
return new Rect(0, 0, 0, 0);
}
private _getLocation (mouseEvent: MouseEvent): Vec2 {
const canvasRect = this._getCanvasRect();
const dpr = screenAdapter.devicePixelRatio;
let x = this._pointLocked ? (this._preMousePos.x / dpr + mouseEvent.movementX) : (mouseEvent.clientX - canvasRect.x);
let y = this._pointLocked ? (this._preMousePos.y / dpr - mouseEvent.movementY) : (canvasRect.y + canvasRect.height - mouseEvent.clientY);
x *= dpr;
y *= dpr;
return new Vec2(x, y);
}
private _registerEvent () {
// register mouse down event
window.addEventListener('mousedown', () => {
this._isPressed = true;
});
this._canvas?.addEventListener('mousedown', this._createCallback(InputEventType.MOUSE_DOWN));
// register mouse move event
this._canvas?.addEventListener('mousemove', this._createCallback(InputEventType.MOUSE_MOVE));
// register mouse up event
const handleMouseUp = this._createCallback(InputEventType.MOUSE_UP);
window.addEventListener('mouseup', handleMouseUp);
this._canvas?.addEventListener('mouseup', handleMouseUp);
// register wheel event
this._canvas?.addEventListener('wheel', this._handleMouseWheel.bind(this));
this._registerPointerLockEvent();
}
// To be removed in the future.
private _registerPointerLockEvent () {
const lockChangeAlert = () => {
const canvas = this._canvas;
// @ts-expect-error undefined mozPointerLockElement
if (document.pointerLockElement === canvas || document.mozPointerLockElement === canvas) {
this._pointLocked = true;
} else {
this._pointLocked = false;
}
};
if ('onpointerlockchange' in document) {
document.addEventListener('pointerlockchange', lockChangeAlert, false);
} else if ('onmozpointerlockchange' in document) {
document.addEventListener('mozpointerlockchange', lockChangeAlert, false);
}
}
private _createCallback (eventType: InputEventType) {
return (mouseEvent: MouseEvent) => {
const location = this._getLocation(mouseEvent);
let button = mouseEvent.button;
switch (eventType) {
case InputEventType.MOUSE_DOWN:
this._canvas?.focus();
this._isPressed = true;
break;
case InputEventType.MOUSE_UP:
this._isPressed = false;
break;
case InputEventType.MOUSE_MOVE:
if (!this._isPressed) {
button = EventMouse.BUTTON_MISSING;
}
break;
default:
break;
}
const eventMouse = new EventMouse(eventType, false, this._preMousePos);
eventMouse.setLocation(location.x, location.y);
eventMouse.setButton(button);
eventMouse.movementX = mouseEvent.movementX;
eventMouse.movementY = mouseEvent.movementY;
// update previous mouse position.
this._preMousePos.set(location.x, location.y);
mouseEvent.stopPropagation();
if (mouseEvent.target === this._canvas) {
mouseEvent.preventDefault();
}
this._eventTarget.emit(eventType, eventMouse);
};
}
private _handleMouseWheel (mouseEvent: WheelEvent) {
const eventType = InputEventType.MOUSE_WHEEL;
const location = this._getLocation(mouseEvent);
const button = mouseEvent.button;
const eventMouse = new EventMouse(eventType, false, this._preMousePos);
eventMouse.setLocation(location.x, location.y);
eventMouse.setButton(button);
eventMouse.movementX = mouseEvent.movementX;
eventMouse.movementY = mouseEvent.movementY;
const wheelSensitivityFactor = 5;
eventMouse.setScrollData(mouseEvent.deltaX * wheelSensitivityFactor, -mouseEvent.deltaY * wheelSensitivityFactor);
// update previous mouse position.
this._preMousePos.set(location.x, location.y);
mouseEvent.stopPropagation();
if (mouseEvent.target === this._canvas) {
mouseEvent.preventDefault();
}
this._eventTarget.emit(eventType, eventMouse);
}
public on (eventType: InputEventType, callback: MouseCallback, target?: any) {
this._eventTarget.on(eventType, callback, target);
}
}