123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- import { Nullable } from "../../types";
- import { serialize } from "../../Misc/decorators";
- import { EventState, Observer } from "../../Misc/observable";
- import { Tools } from "../../Misc/tools";
- import { Camera } from "../../Cameras/camera";
- import { ICameraInput } from "../../Cameras/cameraInputsManager";
- import { PointerInfo, PointerEventTypes, PointerTouch } from "../../Events/pointerEvents";
- /**
- * Base class for Camera Pointer Inputs.
- * See FollowCameraPointersInput in src/Cameras/Inputs/followCameraPointersInput.ts
- * for example usage.
- */
- export abstract class BaseCameraPointersInput implements ICameraInput<Camera> {
- /**
- * Defines the camera the input is attached to.
- */
- public abstract camera: Camera;
- /**
- * Whether keyboard modifier keys are pressed at time of last mouse event.
- */
- protected _altKey: boolean;
- protected _ctrlKey: boolean;
- protected _metaKey: boolean;
- protected _shiftKey: boolean;
- /**
- * Which mouse buttons were pressed at time of last mouse event.
- * https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
- */
- protected _buttonsPressed: number;
- /**
- * Defines the buttons associated with the input to handle camera move.
- */
- @serialize()
- public buttons = [0, 1, 2];
- /**
- * Attach the input controls to a specific dom element to get the input from.
- * @param element Defines the element the controls should be listened from
- * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
- */
- public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
- var engine = this.camera.getEngine();
- var previousPinchSquaredDistance = 0;
- var previousMultiTouchPanPosition: Nullable<PointerTouch> = null;
- this.pointA = null;
- this.pointB = null;
- this._altKey = false;
- this._ctrlKey = false;
- this._metaKey = false;
- this._shiftKey = false;
- this._buttonsPressed = 0;
- this._pointerInput = (p, s) => {
- var evt = <PointerEvent>p.event;
- let isTouch = evt.pointerType === "touch";
- if (engine.isInVRExclusivePointerMode) {
- return;
- }
- if (p.type !== PointerEventTypes.POINTERMOVE &&
- this.buttons.indexOf(evt.button) === -1) {
- return;
- }
- let srcElement = <HTMLElement>(evt.srcElement || evt.target);
- this._altKey = evt.altKey;
- this._ctrlKey = evt.ctrlKey;
- this._metaKey = evt.metaKey;
- this._shiftKey = evt.shiftKey;
- this._buttonsPressed = evt.buttons;
- if (engine.isPointerLock) {
- var offsetX = evt.movementX ||
- evt.mozMovementX ||
- evt.webkitMovementX ||
- evt.msMovementX ||
- 0;
- var offsetY = evt.movementY ||
- evt.mozMovementY ||
- evt.webkitMovementY ||
- evt.msMovementY ||
- 0;
- this.onTouch(null, offsetX, offsetY);
- this.pointA = null;
- this.pointB = null;
- } else if (p.type === PointerEventTypes.POINTERDOWN && srcElement) {
- try {
- srcElement.setPointerCapture(evt.pointerId);
- } catch (e) {
- //Nothing to do with the error. Execution will continue.
- }
- if (this.pointA === null) {
- this.pointA = {x: evt.clientX,
- y: evt.clientY,
- pointerId: evt.pointerId,
- type: evt.pointerType };
- } else if (this.pointB === null) {
- this.pointB = {x: evt.clientX,
- y: evt.clientY,
- pointerId: evt.pointerId,
- type: evt.pointerType };
- }
- this.onButtonDown(evt);
- if (!noPreventDefault) {
- evt.preventDefault();
- element.focus();
- }
- } else if (p.type === PointerEventTypes.POINTERDOUBLETAP) {
- this.onDoubleTap(evt.pointerType);
- } else if (p.type === PointerEventTypes.POINTERUP && srcElement) {
- try {
- srcElement.releasePointerCapture(evt.pointerId);
- } catch (e) {
- //Nothing to do with the error.
- }
- if (!isTouch) {
- this.pointB = null; // Mouse and pen are mono pointer
- }
- //would be better to use pointers.remove(evt.pointerId) for multitouch gestures,
- //but emptying completely pointers collection is required to fix a bug on iPhone :
- //when changing orientation while pinching camera,
- //one pointer stay pressed forever if we don't release all pointers
- //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
- if (engine._badOS) {
- this.pointA = this.pointB = null;
- } else {
- //only remove the impacted pointer in case of multitouch allowing on most
- //platforms switching from rotate to zoom and pan seamlessly.
- if (this.pointB && this.pointA && this.pointA.pointerId == evt.pointerId) {
- this.pointA = this.pointB;
- this.pointB = null;
- } else if (this.pointA && this.pointB &&
- this.pointB.pointerId == evt.pointerId) {
- this.pointB = null;
- } else {
- this.pointA = this.pointB = null;
- }
- }
- if (previousPinchSquaredDistance !== 0 || previousMultiTouchPanPosition) {
- // Previous pinch data is populated but a button has been lifted
- // so pinch has ended.
- this.onMultiTouch(
- this.pointA,
- this.pointB,
- previousPinchSquaredDistance,
- 0, // pinchSquaredDistance
- previousMultiTouchPanPosition,
- null // multiTouchPanPosition
- );
- previousPinchSquaredDistance = 0;
- previousMultiTouchPanPosition = null;
- }
- this.onButtonUp(evt);
- if (!noPreventDefault) {
- evt.preventDefault();
- }
- } else if (p.type === PointerEventTypes.POINTERMOVE) {
- if (!noPreventDefault) {
- evt.preventDefault();
- }
- // One button down
- if (this.pointA && this.pointB === null) {
- var offsetX = evt.clientX - this.pointA.x;
- var offsetY = evt.clientY - this.pointA.y;
- this.onTouch(this.pointA, offsetX, offsetY);
- this.pointA.x = evt.clientX;
- this.pointA.y = evt.clientY;
- }
- // Two buttons down: pinch
- else if (this.pointA && this.pointB) {
- var ed = (this.pointA.pointerId === evt.pointerId) ?
- this.pointA : this.pointB;
- ed.x = evt.clientX;
- ed.y = evt.clientY;
- var distX = this.pointA.x - this.pointB.x;
- var distY = this.pointA.y - this.pointB.y;
- var pinchSquaredDistance = (distX * distX) + (distY * distY);
- var multiTouchPanPosition = {x: (this.pointA.x + this.pointB.x) / 2,
- y: (this.pointA.y + this.pointB.y) / 2,
- pointerId: evt.pointerId,
- type: p.type};
- this.onMultiTouch(
- this.pointA,
- this.pointB,
- previousPinchSquaredDistance,
- pinchSquaredDistance,
- previousMultiTouchPanPosition,
- multiTouchPanPosition);
- previousMultiTouchPanPosition = multiTouchPanPosition;
- previousPinchSquaredDistance = pinchSquaredDistance;
- }
- }
- };
- this._observer = this.camera.getScene().onPointerObservable.add(
- this._pointerInput,
- PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP |
- PointerEventTypes.POINTERMOVE);
- this._onLostFocus = () => {
- this.pointA = this.pointB = null;
- previousPinchSquaredDistance = 0;
- previousMultiTouchPanPosition = null;
- this.onLostFocus();
- };
- element.addEventListener("contextmenu",
- <EventListener>this.onContextMenu.bind(this), false);
- let hostWindow = this.camera.getScene().getEngine().getHostWindow();
- if (hostWindow) {
- Tools.RegisterTopRootEvents(hostWindow, [
- { name: "blur", handler: this._onLostFocus }
- ]);
- }
- }
- /**
- * Detach the current controls from the specified dom element.
- * @param element Defines the element to stop listening the inputs from
- */
- public detachControl(element: Nullable<HTMLElement>): void {
- if (this._onLostFocus) {
- let hostWindow = this.camera.getScene().getEngine().getHostWindow();
- if (hostWindow) {
- Tools.UnregisterTopRootEvents(hostWindow, [
- { name: "blur", handler: this._onLostFocus }
- ]);
- }
- }
- if (element && this._observer) {
- this.camera.getScene().onPointerObservable.remove(this._observer);
- this._observer = null;
- if (this.onContextMenu) {
- element.removeEventListener("contextmenu", <EventListener>this.onContextMenu);
- }
- this._onLostFocus = null;
- }
- this._altKey = false;
- this._ctrlKey = false;
- this._metaKey = false;
- this._shiftKey = false;
- this._buttonsPressed = 0;
- }
- /**
- * Gets the class name of the current input.
- * @returns the class name
- */
- public getClassName(): string {
- return "BaseCameraPointersInput";
- }
- /**
- * Get the friendly name associated with the input class.
- * @returns the input friendly name
- */
- public getSimpleName(): string {
- return "pointers";
- }
- /**
- * Called on pointer POINTERDOUBLETAP event.
- * Override this method to provide functionality on POINTERDOUBLETAP event.
- */
- protected onDoubleTap(type: string) {
- }
- /**
- * Called on pointer POINTERMOVE event if only a single touch is active.
- * Override this method to provide functionality.
- */
- protected onTouch(point: Nullable<PointerTouch>,
- offsetX: number,
- offsetY: number): void {
- }
- /**
- * Called on pointer POINTERMOVE event if multiple touches are active.
- * Override this method to provide functionality.
- */
- protected onMultiTouch(pointA: Nullable<PointerTouch>,
- pointB: Nullable<PointerTouch>,
- previousPinchSquaredDistance: number,
- pinchSquaredDistance: number,
- previousMultiTouchPanPosition: Nullable<PointerTouch>,
- multiTouchPanPosition: Nullable<PointerTouch>): void {
- }
- /**
- * Called on JS contextmenu event.
- * Override this method to provide functionality.
- */
- protected onContextMenu(evt: PointerEvent): void {
- evt.preventDefault();
- }
- /**
- * Called each time a new POINTERDOWN event occurs. Ie, for each button
- * press.
- * Override this method to provide functionality.
- */
- protected onButtonDown(evt: PointerEvent): void {
- }
- /**
- * Called each time a new POINTERUP event occurs. Ie, for each button
- * release.
- * Override this method to provide functionality.
- */
- protected onButtonUp(evt: PointerEvent): void {
- }
- /**
- * Called when window becomes inactive.
- * Override this method to provide functionality.
- */
- protected onLostFocus(): void {
- }
- private _pointerInput: (p: PointerInfo, s: EventState) => void;
- private _observer: Nullable<Observer<PointerInfo>>;
- private _onLostFocus: Nullable<(e: FocusEvent) => any>;
- private pointA: Nullable<PointerTouch>;
- private pointB: Nullable<PointerTouch>;
- }
|