@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
298 lines • 13.7 kB
JavaScript
import { __decorate } from "../../tslib.es6.js";
import { serialize } from "../../Misc/decorators.js";
import { CameraInputTypes } from "../../Cameras/cameraInputsManager.js";
import { KeyboardEventTypes } from "../../Events/keyboardEvents.js";
import { Tools } from "../../Misc/tools.js";
/**
* Manage the keyboard inputs to control the movement of an arc rotate camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
*/
export class ArcRotateCameraKeyboardMoveInput {
constructor() {
/**
* Defines the list of key codes associated with the up action (increase alpha)
*/
this.keysUp = [38];
/**
* Defines the list of key codes associated with the down action (decrease alpha)
*/
this.keysDown = [40];
/**
* Defines the list of key codes associated with the left action (increase beta)
*/
this.keysLeft = [37];
/**
* Defines the list of key codes associated with the right action (decrease beta)
*/
this.keysRight = [39];
/**
* Defines the list of key codes associated with the reset action.
* Those keys reset the camera to its last stored state (with the method camera.storeState())
*/
this.keysReset = [220];
/**
* Defines the list of key codes associated with the zoom in action.
* Only used when CameraMovement is active — these keys always trigger zoom regardless of modifiers.
*/
this.keysZoomIn = [187, 107]; // 187 = +/= key, 107 = numpad +
/**
* Defines the list of key codes associated with the zoom out action.
* Only used when CameraMovement is active — these keys always trigger zoom regardless of modifiers.
*/
this.keysZoomOut = [189, 109]; // 189 = -/_ key, 109 = numpad -
/**
* Defines the panning sensibility of the inputs.
* (How fast is the camera panning)
*/
this.panningSensibility = 50.0;
/**
* Defines the zooming sensibility of the inputs.
* (How fast is the camera zooming)
*/
this.zoomingSensibility = 25.0;
/**
* Rotation speed of the camera
*/
this.angularSpeed = 0.01;
this._useAltToZoom = true;
this._keys = new Array();
/**
* Modifier state stored separately from `_keyboardConditions` so it can be typed as a
* concrete (non-optional) object. This avoids non-null assertions when updating modifier
* fields each frame, and the conditions object holds the same reference so
* resolveInteraction sees the live state.
*/
this._keyboardModifiers = { ctrl: false, alt: false };
/** Cached conditions object to avoid per-frame allocations in checkInputs */
this._keyboardConditions = { modifiers: this._keyboardModifiers };
}
/**
* Defines whether alt+arrows/wasd triggers zoom instead of rotation/pan.
* When disabled, alt+keyboard events are ignored by the zoom inputMap entry.
* Setting this updates the corresponding inputMap entry on the camera's movement system.
* If set before the camera is attached, the value is cached and applied during `attachControl`.
*/
get useAltToZoom() {
return this._useAltToZoom;
}
set useAltToZoom(value) {
this._useAltToZoom = value;
this._applyUseAltToZoomToInputMap();
}
/**
* Applies the cached `_useAltToZoom` value to the camera's inputMap.
* Safe to call before the camera is attached: it is a no-op until `this.camera.movement` is available.
* Idempotent — calling it when the inputMap already matches the cached value is a no-op.
*/
_applyUseAltToZoomToInputMap() {
if (!this.camera?.movement) {
return;
}
const input = this.camera.movement.input;
const entry = input.getEntry("keyboard", "zoom", { modifiers: { alt: true } });
if (!this._useAltToZoom && entry) {
input.inputMap.splice(input.inputMap.indexOf(entry), 1);
}
else if (this._useAltToZoom && !entry) {
input.addEntry({ source: "keyboard", modifiers: { alt: true }, interaction: "zoom" });
}
}
/**
* Attach the input controls to a specific dom element to get the input from.
* @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
*/
attachControl(noPreventDefault) {
// was there a second variable defined?
noPreventDefault = Tools.BackCompatCameraNoPreventDefault(arguments);
if (this._onCanvasBlurObserver) {
return;
}
this._scene = this.camera.getScene();
this._engine = this._scene.getEngine();
this._applyUseAltToZoomToInputMap();
this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
this._keys.length = 0;
});
this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
const evt = info.event;
if (!evt.metaKey) {
if (info.type === KeyboardEventTypes.KEYDOWN) {
this._ctrlPressed = evt.ctrlKey;
this._altPressed = evt.altKey;
if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
this.keysDown.indexOf(evt.keyCode) !== -1 ||
this.keysLeft.indexOf(evt.keyCode) !== -1 ||
this.keysRight.indexOf(evt.keyCode) !== -1 ||
this.keysReset.indexOf(evt.keyCode) !== -1 ||
this.keysZoomIn.indexOf(evt.keyCode) !== -1 ||
this.keysZoomOut.indexOf(evt.keyCode) !== -1) {
const index = this._keys.indexOf(evt.keyCode);
if (index === -1) {
this._keys.push(evt.keyCode);
}
if (evt.preventDefault) {
if (!noPreventDefault) {
evt.preventDefault();
}
}
}
}
else {
if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
this.keysDown.indexOf(evt.keyCode) !== -1 ||
this.keysLeft.indexOf(evt.keyCode) !== -1 ||
this.keysRight.indexOf(evt.keyCode) !== -1 ||
this.keysReset.indexOf(evt.keyCode) !== -1 ||
this.keysZoomIn.indexOf(evt.keyCode) !== -1 ||
this.keysZoomOut.indexOf(evt.keyCode) !== -1) {
const index = this._keys.indexOf(evt.keyCode);
if (index >= 0) {
this._keys.splice(index, 1);
}
if (evt.preventDefault) {
if (!noPreventDefault) {
evt.preventDefault();
}
}
}
}
}
});
}
/**
* Detach the current controls from the specified dom element.
*/
detachControl() {
if (this._scene) {
if (this._onKeyboardObserver) {
this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
}
if (this._onCanvasBlurObserver) {
this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
}
this._onKeyboardObserver = null;
this._onCanvasBlurObserver = null;
}
this._keys.length = 0;
}
/**
* Update the current camera state depending on the inputs that have been used this frame.
* This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
*/
checkInputs() {
if (this._onKeyboardObserver) {
const camera = this.camera;
const input = camera.movement.input;
this._keyboardModifiers.ctrl = this._ctrlPressed;
this._keyboardModifiers.alt = this._altPressed;
for (let index = 0; index < this._keys.length; index++) {
const keyCode = this._keys[index];
this._keyboardConditions.key = keyCode;
// Skip resolveInteraction for the reset key — it has no inputMap entry of its own
// and would otherwise spuriously match the catch-all keyboard→rotate entry.
if (this.keysReset.indexOf(keyCode) === -1) {
const resolved = input.resolveInteraction("keyboard", this._keyboardConditions);
if (resolved) {
// Per-frame impulse magnitude. The inputMap entry's `sensitivity` takes precedence
// when set so consumers can tune feel declaratively (and so we can phase out the
// legacy sensibility/angularSpeed properties over time). When `sensitivity` is
// undefined, fall back to the legacy properties for backward compatibility.
if (resolved.interaction === "pan") {
const panSens = resolved.sensitivity ?? 1 / this.panningSensibility;
if (this.keysLeft.indexOf(keyCode) !== -1) {
input.handlers.pan(-panSens, 0);
}
else if (this.keysRight.indexOf(keyCode) !== -1) {
input.handlers.pan(panSens, 0);
}
else if (this.keysUp.indexOf(keyCode) !== -1) {
input.handlers.pan(0, panSens);
}
else if (this.keysDown.indexOf(keyCode) !== -1) {
input.handlers.pan(0, -panSens);
}
}
else if (resolved.interaction === "zoom") {
const zoomSens = resolved.sensitivity ?? 1 / this.zoomingSensibility;
if (this.keysUp.indexOf(keyCode) !== -1 || this.keysZoomIn.indexOf(keyCode) !== -1) {
input.handlers.zoom(zoomSens);
}
else if (this.keysDown.indexOf(keyCode) !== -1 || this.keysZoomOut.indexOf(keyCode) !== -1) {
input.handlers.zoom(-zoomSens);
}
}
else if (resolved.interaction === "rotate") {
const rotateSens = resolved.sensitivity ?? this.angularSpeed;
if (this.keysLeft.indexOf(keyCode) !== -1) {
input.handlers.rotate(-rotateSens, 0);
}
else if (this.keysRight.indexOf(keyCode) !== -1) {
input.handlers.rotate(rotateSens, 0);
}
else if (this.keysUp.indexOf(keyCode) !== -1) {
input.handlers.rotate(0, -rotateSens);
}
else if (this.keysDown.indexOf(keyCode) !== -1) {
input.handlers.rotate(0, rotateSens);
}
}
}
}
if (this.keysReset.indexOf(keyCode) !== -1) {
if (camera.useInputToRestoreState) {
camera.restoreState();
}
}
}
}
}
/**
* Gets the class name of the current input.
* @returns the class name
*/
getClassName() {
return "ArcRotateCameraKeyboardMoveInput";
}
/**
* Get the friendly name associated with the input class.
* @returns the input friendly name
*/
getSimpleName() {
return "keyboard";
}
}
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "keysUp", void 0);
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "keysDown", void 0);
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "keysLeft", void 0);
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "keysRight", void 0);
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "keysReset", void 0);
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "keysZoomIn", void 0);
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "keysZoomOut", void 0);
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "panningSensibility", void 0);
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "zoomingSensibility", void 0);
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "angularSpeed", void 0);
__decorate([
serialize()
], ArcRotateCameraKeyboardMoveInput.prototype, "useAltToZoom", null);
CameraInputTypes["ArcRotateCameraKeyboardMoveInput"] = ArcRotateCameraKeyboardMoveInput;
//# sourceMappingURL=arcRotateCameraKeyboardMoveInput.js.map