[SOLVED] PlayCanvas model viewer block keyboard controls while using text inputs

Im trying to block the camera keyboard controls used by the multicamera but it didnt worked, I tried to every time an input field or input textarea is selected to block the controls.

        document.querySelectorAll('input, textarea').forEach((input) => {
            input.addEventListener('focus', () => {
                console.log('Input focused');
                this.isInputFocused = true;
                if (window.viewer && window.viewer.app && window.viewer.app.keyboard) {
                    window.viewer.app.keyboard.off('keydown'); 
                }
            });

            input.addEventListener('blur', () => {
                console.log('Input blurred');
                this.isInputFocused = false;
                if (window.viewer && window.viewer.app && window.viewer.app.keyboard) {
                    window.viewer.app.keyboard.on('keydown', (event) => {
                        console.log('Reattaching keyboard controls');
                    });
                }
            });
        });

And also in the update(deltaTime) I tried to use another extra check.

        if (!this.xrMode?.active && !this.isInputFocused) {
            this.multiCamera.update(deltaTime);
        }

Both seems to work properly after some console.log debugging, but the camera is still moving, maybe is something with the multiCamera that model viewer uses but I have no idea.

Anyone could help me??

Are you talking about the SuperSplat Viewer?

Nope, I mean the playcanvas model viewer :))

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);
    }

Thank you so much for the reply!! I already made up with a solution just changing the moveSpeed

        document.querySelectorAll('input, textarea').forEach((input) => {
            input.addEventListener('focus', () => {
                console.log('Input focused');
                this.isInputFocused = true;
                this.multiCamera.moveSpeed = 0; // Disable camera movement
            });

            input.addEventListener('blur', () => {
                console.log('Input blurred');
                this.isInputFocused = false;
                this.multiCamera.moveSpeed = 0.05;
            });
        });

I hope this helps anyone! Thanks for the reply again :)))

1 Like