OK.
So this is basically a generic problem with the Model Viewer. If I open the color picker to change the camera clear color, for example, and click in a text input and press the arrow keys to move the cursor, the camera moves left and right too.
One fix for that could be to update CameraControls#update to check the active DOM element and disable camera movement if the active element is an input element.
Something like:
update(dt: number) {
const { keyCode } = KeyboardMouseSource;
const { key, button, mouse, wheel } = this._desktopInput.read();
const { touch, pinch, count } = this._orbitMobileInput.read();
const { leftInput, rightInput } = this._flyMobileInput.read();
const { leftStick, rightStick } = this._gamepadInput.read();
// check if user is typing in an input element
const activeElement = document.activeElement;
const isTyping = activeElement && (
activeElement.tagName === 'INPUT' ||
activeElement.tagName === 'TEXTAREA' ||
activeElement.tagName === 'SELECT' ||
(activeElement as HTMLElement).isContentEditable
);
// apply dead zone to gamepad sticks
applyDeadZone(leftStick, this.gamepadDeadZone.x, this.gamepadDeadZone.y);
applyDeadZone(rightStick, this.gamepadDeadZone.x, this.gamepadDeadZone.y);
// update state (skip keyboard input if user is typing in an input element)
if (!isTyping) {
this._state.axis.add(tmpV1.set(
(key[keyCode.D] - key[keyCode.A]) + (key[keyCode.RIGHT] - key[keyCode.LEFT]),
(key[keyCode.E] - key[keyCode.Q]),
(key[keyCode.W] - key[keyCode.S]) + (key[keyCode.UP] - key[keyCode.DOWN])
));
this._state.shift += key[keyCode.SHIFT];
this._state.ctrl += key[keyCode.CTRL];
}
for (let i = 0; i < this._state.mouse.length; i++) {
this._state.mouse[i] += button[i];
}
this._state.touches += count[0];
if (this._mode !== 'fly' && this._state.axis.length() > 0) {
// if we have any axis input, switch to fly mode
this.mode = 'fly';
}
const orbit = +(this._mode === 'orbit');
const fly = +(this._mode === 'fly');
const double = +(this._state.touches > 1);
const pan = this._state.mouse[2] || +(button[2] === -1) || double;
const distance = this._pose.distance;
const { deltas } = frame;
// desktop move
const v = tmpV1.set(0, 0, 0);
const keyMove = this._state.axis.clone().normalize();
v.add(keyMove.mulScalar(fly * this.moveSpeed * (this._state.shift ? 2 : this._state.ctrl ? 0.5 : 1) * dt));
const panMove = screenToWorld(this._camera, mouse[0], mouse[1], distance);
v.add(panMove.mulScalar(pan));
const wheelMove = new Vec3(0, 0, -wheel[0]);
v.add(wheelMove.mulScalar(this.wheelSpeed * dt));
// FIXME: need to flip z axis for orbit camera
deltas.move.append([v.x, v.y, orbit ? -v.z : v.z]);
// desktop rotate
v.set(0, 0, 0);
const mouseRotate = new Vec3(mouse[0], mouse[1], 0);
v.add(mouseRotate.mulScalar((1 - pan) * this.orbitSpeed * dt));
deltas.rotate.append([v.x, v.y, v.z]);
// mobile move
v.set(0, 0, 0);
const orbitMove = screenToWorld(this._camera, touch[0], touch[1], distance);
v.add(orbitMove.mulScalar(orbit * pan));
const flyMove = new Vec3(leftInput[0], 0, -leftInput[1]);
v.add(flyMove.mulScalar(fly * this.moveSpeed * dt));
const pinchMove = new Vec3(0, 0, pinch[0]);
v.add(pinchMove.mulScalar(orbit * double * this.pinchSpeed * dt));
deltas.move.append([v.x, v.y, v.z]);
// mobile rotate
v.set(0, 0, 0);
const orbitRotate = new Vec3(touch[0], touch[1], 0);
v.add(orbitRotate.mulScalar(orbit * (1 - pan) * this.orbitSpeed * dt));
const flyRotate = new Vec3(rightInput[0], rightInput[1], 0);
v.add(flyRotate.mulScalar(fly * this.orbitSpeed * dt));
deltas.rotate.append([v.x, v.y, v.z]);
// gamepad move
v.set(0, 0, 0);
const stickMove = new Vec3(leftStick[0], 0, -leftStick[1]);
v.add(stickMove.mulScalar(this.moveSpeed * dt));
deltas.move.append([v.x, v.y, v.z]);
// gamepad rotate
v.set(0, 0, 0);
const stickRotate = new Vec3(rightStick[0], rightStick[1], 0);
v.add(stickRotate.mulScalar(this.orbitSpeed * dt));
deltas.rotate.append([v.x, v.y, v.z]);
// check if XR is active, just read frame to clear it
if (this._app.xr?.active) {
frame.read();
return;
}
// update controller by consuming frame
this._pose.copy(this._controller.update(frame, dt));
this._camera.entity.setPosition(this._pose.position);
this._camera.entity.setEulerAngles(this._pose.angles);
}