Unresponsive Three.js OrbitControls

*Update: I'm feeling a bit confused because I don't believe my function is causing this issue. It seems that simply double-clicking without moving the mouse triggers this behavior consistently, even in the Three.js example.

I'm still unsure how to eliminate this behavior or if it's a deliberate feature.


Whenever I double-click on the scene, a function is triggered to smoothly reset the camera to its original position. However, after the reset, any attempt to move with OrbitControls results in the cursor changing to not-allowed and the controls barely rotate before coming to a halt. The only way to regain control is by single-clicking on the canvas without dragging, which resets the controls to a functional state.

When I remove the resetPosition function, which gradually moves the camera back to the origin, the controls mostly work as intended. I've checked the code and can't identify any obvious cause.

*A similar issue occurs inconsistently when double-clicking, especially when quickly dragging the controls on the second click, even without resetPosition. The presence of resetPosition just makes the issue more predictable. This behavior can be observed even in this official Three.js example.

View an image of the issue, as I can't directly post images on StackOverflow

How can I prevent this issue? I've come across several Three.js examples that don't have this problem, but I'm unsure what sets them apart.

Here is the relevant code snippet:

this.controls = new OrbitControls(this.camera, this.element)
this.controls.target.set(CONTROLS_TARGET.x, CONTROLS_TARGET.y, CONTROLS_TARGET.z)
this.controls.userRotateSpeed = 3
this.controls.maxDistance = 200
this.controls.minDistance = 10
resetPosition() {
    let azimuthalAngle = this.controls.getAzimuthalAngle(),
        polarAngle = this.controls.getPolarAngle() - Math.PI / 2,
        distance = this.controls.getDistance() - 30,
        movement = this.controls.target.clone()

    let lerpDelta = LERP_ALPHA ** (1 + this.deltaTime * 60)

    if (Math.abs(azimuthalAngle) < 0.001) azimuthalAngle = 0
    if (Math.abs(polarAngle) < 0.001) polarAngle = 0
    if (Math.abs(distance) < 0.001 && Math.abs(distance) > -0.001) distance = 0
    if (movement.distanceTo(CONTROLS_TARGET) < 0.05) movement = CONTROLS_TARGET

    this.controls.minAzimuthAngle = lerpDelta * azimuthalAngle
    this.controls.maxAzimuthAngle = this.controls.minAzimuthAngle

    this.controls.minPolarAngle = Math.PI/2 + lerpDelta * polarAngle
    this.controls.maxPolarAngle = this.controls.minPolarAngle

    this.controls.minDistance = lerpDelta * distance + 30
    this.controls.maxDistance = this.controls.minDistance

    movement.lerp(CONTROLS_TARGET, 1.0 - lerpDelta)
    this.controls.target.set(movement.x, movement.y, movement.z)

    if(azimuthalAngle === 0
        && polarAngle === 0
        && distance === 0
        && movement.equals(CONTROLS_TARGET)
    ) this.finishReset()
}
finishReset() {
    this.controls.minAzimuthAngle = -Infinity
    this.controls.maxAzimuthAngle = Infinity
    this.controls.minPolarAngle = 0
    this.controls.maxPolarAngle = Math.PI
    this.controls.minDistance = 10
    this.controls.maxDistance = 200
    this.doReset = false
}
animate() {
    if (this.doReset) this.resetPosition()
    this.controls.update()
    
    this.render()
}

Answer №1

You may be experiencing difficulties due to how the resetPosition() function is being handled in conjunction with continuous updates to the OrbitControls during the animation loop. When you double-click on the scene, the resetPosition() function is triggered, smoothly resetting the camera position. This process involves adjusting the min/max azimuthal and polar angles, as well as the min/max distance of the OrbitControls to prevent the camera from moving during the reset.

The issue arises when these constraints are continuously modified during the animation loop. The resetPosition() function is being called on every frame when this.doReset is true, potentially disrupting the smooth operation of the OrbitControls and leading to the described undesired behavior.

To resolve this, consider implementing the following changes:

  1. Ensure that the resetPosition() function is only called once when double-clicking to reset the camera. Use a flag to signal the start of the reset process and clear the flag once the reset is completed.

  2. Avoid continuously updating the min/max azimuthal and polar angles, as well as min/max distance, within the resetPosition() function. Instead, set these constraints during the OrbitControls initialization phase.

Below is an altered version of your code:

// Initialization code
this.controls = new OrbitControls(this.camera, this.element);
this.controls.target.set(CONTROLS_TARGET.x, CONTROLS_TARGET.y, CONTROLS_TARGET.z);
this.controls.userRotateSpeed = 3;
this.controls.maxDistance = 200;
this.controls.minDistance = 10;

// Flag to trigger reset process
this.doReset = false;

// Function to handle double-click event
function onDoubleClick() {
  this.doReset = true;
}

// Double-click event listener on canvas
this.element.addEventListener('dblclick', onDoubleClick.bind(this));

// Function to reset camera position
function resetPosition() {
  // Reset logic here
}

function finishReset() {
  // Code to finalize reset
}

function animate() {
  if (this.doReset) {
    resetPosition.call(this);
  }
  this.controls.update();
  this.render();
}

// Add animate function call in main animation loop

Ensuring that the resetPosition() function is only triggered once during double-click events and setting constraints during initialization should enable smooth operation of the OrbitControls without encountering the cursor issue.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Feature for jotting down notes, creating a break between lines without any information present

I'm currently working on implementing a note-taking feature for my web application. However, I have encountered two issues: Firstly, I am struggling to identify line breaks within the text. While I can hardcode text with line breaks, when a user clic ...

When a page with parameters is reloaded, React fails to initiate

I'm encountering an issue with my application in development mode on localhost. When I try to reload the app on a route with parameters, for example: localhost:8080/item/2 React fails to initialize and only a blank page is displayed. However, if I ...

React - Refreshing a component with the help of another component

I've created a NavBar component that contains a list of links generated dynamically. These links are fetched from the backend based on specific categories and are stored within a child component of NavBar named DrawerMenu. The NavBar itself is a chil ...

Chrome extensions using Cross-Origin XMLHttpRequest

Chrome extensions API states that cross-origin calls using the XMLHttpRequest object should be allowed with proper permissions: An extension can communicate with remote servers beyond its origin by requesting cross-origin permissions. Following the Goog ...

Tips for creating a fading effect when hovering over a div element

Hello, I am currently working on a project where I have a span that displays over an image on hover. I would like to add a bit of javascript or css transitions to make this div fade in to about 0.8 opacity when hovered over, and then fade back to 0 opacity ...

Selecting dates based on week number

Is there a method in asp.net or via javascript to capture the dates (Monday to Friday) when a user clicks on the week number displayed on the calendar and display them on a label? ...

Tips for handling Promise.all and waiting for all promises to resolve in an async function in Express JS

I'm relatively new to JavaScript and especially asynchronous programming. My current project involves creating an Express+React application that shows a GitHub user's information, including a few repositories with the latest 5 commits for each. ...

Increase the options available in the dropdown menu by adding more selected items, without removing any already selected

I came across this code on the internet http://jsfiddle.net/bvotcode/owhq5jat/ When I select a new item, the old item is replaced by the new item. How can I add more items without replacing them when I click "dropdown list"? Thank you <select id=&qu ...

Converting Greek text to UTF-8 using Javascript

Currently, I am working on a project with my teacher to digitalize a Greek textbook by transforming it into an online application. This process involves the conversion of a Shapefile, which draws polygons on maps along with polygon descriptions, and mappin ...

angular5: The ngFor directive will only function properly after the second button click

Here is my current situation: 1) When the user inputs a keyword in a text field and clicks on the search icon, it triggers an HTTP request to retrieve the data. 2) The retrieved data is then rendered in HTML using ngFor. The issue I am facing is that up ...

Personalized information boxes for particular data points within highcharts

When hovering over specific points, I want to display unique warnings or explanations in the tooltip. I have included custom text like 'WARNING' and 'WARNING 2' within the series data but am struggling to retrieve that data for each too ...

What is the threading model utilized by node.js?

Upon establishing a connection with a server operating on node.js, how is the connection handled? Is it one connection per process? Does it follow a connection per thread model? Or does it operate based on request per thread? Alternatively, does it use ...

Issue with implementing MUI Style Tag in conjunction with styled-components and Typescript

I have created a custom SelectType component using Styled Components, which looks like this: import Select from '@mui/material/Select'; export const SelectType = styled(Select)` width:100%; border:2px solid #eaeaef; border-radius:8px ...

Steps to dynamically adjust an element's width in CSS depending on the presence of a separate element

I have a text input box appearing on the left side with a button positioned next to it on the right. For certain pages, I want to display a checkbox next to the input box and move the button below it. Is there a way to achieve this using CSS flexbox or a ...

Tips for incorporating an outside model into vue.js with babylon js

Struggling with importing a gltf file into vue.js using babylon.js to create a 3D view on a webpage. The documentation online isn't very clear, and I've tried the following steps in my Hello.vue file: <div> <h1> Hello </h1> < ...

The functionality of JavaScript on an icon that is supposed to expand and collapse a table is not functioning properly when the enter

On this particular page, I have a toggleable table of information that expands and collapses when the user clicks on a clickable +/- icon. However, there seems to be an issue where if the user tabs to the icon and tries to expand/collapse it by pressing en ...

Highlighting a Table Column with a Radio Button

I am struggling with controlling the highlight of a table using only radio buttons. When I try to change the selector to input:radio, it doesn't work as expected. It seems to only work with the selector provided in my code snippet. $("th").on("clic ...

Navigating through Node's asynchronous behavior

I am currently developing a web scraper that gathers data about various shirts from a specific website. I have successfully set up all the necessary NPM packages in Node.js to scrape the information and save it to a CSV file. However, I have encountered ...

Error: The property 'target' cannot be read

I'm seeking to enhance a value by pinpointing a specific element within a loop. <div *ngFor="let item of items; let i = index"> <ion-button (click)="increment(i)"> <ion-icon name="add"></ion ...

Utilize Node.js and Socket.IO to download a PNG image in a web browser

Looking for assistance in saving an image from Flash to a server using node.js. Below is my code snippet: var pngEncoder:PNGEncoder = new PNGEncoder(); var pngStream:ByteArray = PNGEncoder.encode(bmd); socket.send(pngStream,"image"); Above is the Flash c ...