What are the steps to manipulate a scene using three.js?

This HTML page showcases a scene with various points that can be comfortably zoomed in using the mouse wheel.

However, I am looking to implement the functionality to drag the scene after zooming in. The idea is to click and hold the left mouse button while moving the mouse, causing the entire scene (or camera) to move accordingly by adjusting x/y coordinates.

I attempted to set up a listener to capture clicks, but it does not produce any output in the console when clicking. Additionally, there was a suggestion to use DragControls, but it appears to be undefined in THREE as uncommenting those lines leads to errors.

So, the question remains: how can I enable dragging of the entire scene (or camera)?

Code:

<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<html>
    <head>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
        <title>Test</title>
    </head>
    <body>
        <script>
            container = document.createElement('div');
            document.body.appendChild(container);
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
            camera.position.set(0, 0, 150);
            scene = new THREE.Scene();
            scene.add(camera);
            renderer = new THREE.WebGLRenderer({
                clearAlpha: 1
            });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setClearColor(0x228B22, 1);
            document.body.appendChild(renderer.domElement); 
            
            // Define a standard Circle
            circle = new THREE.CircleGeometry(1, 20);
            
            var max = 50;
            var min = -50;              
            for (var i = 0; i < 100; i++) {         
                var object = new THREE.Mesh( circle.clone(), new THREE.MeshBasicMaterial( { color: new THREE.Color('yellow'), opacity: 0.5 } ) );
                object.position.x = Math.random() * (max - min) + min;
                object.position.y = Math.random() * (max - min) + min;
                object.position.z = 0;                  
                scene.add( object );            
            }
            
            document.addEventListener( 'mousewheel', onDocumentMouseWheel, false );
            function onDocumentMouseWheel( event ) {
                var fovMAX = 100;
                var fovMIN = 1;
                camera.fov -= event.wheelDeltaY * 0.05;
                camera.fov = Math.max( Math.min( camera.fov, fovMAX ), fovMIN );
                camera.projectionMatrix = new THREE.Matrix4().makePerspective(camera.fov, window.innerWidth / window.innerHeight, camera.near, camera.far);
            }
            
            document.addEventListener( 'mouseclick', onDocumentMouseClick, false );
            function onDocumentMouseClick( event ) {
                console.log("mouseclick! " + event.offsetX + "-" + event.offsetY, );
            }
            
            animate();
            function animate() {
                requestAnimationFrame(animate);
                renderer.render(scene, camera);
            }
            
            //// undefined: 
            //var controls = new THREE.DragControls( objects, camera, renderer.domElement );            
            //controls.addEventListener( 'dragstart', function ( event ) {
            //  event.object.material.emissive.set( 0xaaaaaa );
            //} );
            //
            //controls.addEventListener( 'dragend', function ( event ) {
            //  event.object.material.emissive.set( 0x000000 );
            //} );
        </script>
    </body>
</html>

Answer №1

There might be alternative approaches, but I managed to find a solution (outlined below). The key is to utilize a flag that indicates whether the mouse button is pressed, implement an algorithm to calculate the difference in position relative to the previous point, and adjust the camera position accordingly.

let mousePressed = false;
let lastMousePos = [0, 0];
let initialCameraPos = 0;

document.addEventListener('mousedown', onMouseDown, false);
function onMouseDown(event) {
    mousePressed = true;
    lastMousePos = [event.offsetX, event.offsetY];
    initialCameraPos = camera.position;
}
document.addEventListener('mouseup', onMouseUp, false);
function onMouseUp(event) {
    mousePressed = false;
}
document.addEventListener('mousemove', onMouseMove, false);
function onMouseMove(event) {
    if (mousePressed) {
        const FOVScaleFactor = Math.tan(camera.fov / 2 * Math.PI / 180) / 1.5;
        let dx = lastMousePos[0] - event.offsetX;
        let dy = lastMousePos[1] - event.offsetY;
        let x = initialCameraPos.x + FOVScaleFactor * dx;
        let y = initialCameraPos.y - FOVScaleFactor * dy;
        camera.position.x = x;
        camera.position.y = y;
        lastMousePos = [event.offsetX, event.offsetY];
        initialCameraPos = camera.position;
    }
}

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

Preventing background style from taking precedence over backgroundColor style in React's inline styling

What causes background to take precedence over backgroundColor in React inline-style? In this scenario, the lack of a specified green background-color results in the background gradient with transparency transitioning into white rather than green within t ...

Nothing remains after the fall: coding void

I am facing an issue where my item becomes null after being dragged 2-3 times and dropped in a different place. I have included my code below and I can't seem to figure out where the mistake lies. Can you please review it and let me know what needs to ...

Ways to retrieve the baseURL of an axios instance

This question is being posted to provide an easy solution for fellow developers who may be looking for an answer. //Suppose you have an axios instance declared in a module called api.js like this: var axios = require('axios'); var axiosInstance ...

Is there an easy method for sorting through JSON data with various criteria?

After receiving a response from my web service containing an array of objects, I am looking to filter the data based on various combinations of values. What is the most efficient way to do this without impacting the performance of my web service? Data: Th ...

What is the best way to implement a switch case for the value of a property within an object in a TypeScript file?

The object I'm dealing with looks like this: {a: auth?.type === '1' || auth?.type === '2' || auth?.type === '3' ? { reason: // I need to implement a switch case here : un ...

I rely on the handleChange function to update the state value, but unfortunately, it remains unchanged

In my project, I am working on creating multiple responsive forms (form1, form2, and form3) within the same page using framer motion. However, I am facing an issue where the state value is not updating correctly when users fill out the form. Specifically, ...

Is there a way to use jQuery to automatically make a field stand out visually?

I am looking for a way to make a text field automatically underline as the user types. The line should follow along with the characters in real-time, and when the user moves away from the field, I want the line to fill up the entire width. This signifies t ...

Encapsulate the HTTP request within a common function for reus

My rest-provider service handles all of my http calls, but I've noticed that each function is repeating a lot of the same http code. It seems like I should create a shared function to avoid this repetition and make it easier to update in the future. ...

How come emphasizing certain characters in a string doesn't display the <b> tag in React.js?

I am trying to make a specific part of a string bold in my React.js app, but I'm not getting the expected result. Below is the code I am using and the output it produces: data?.map((item) => (<li key={item.id}>{item.title.replace(new RegExp(v ...

Setting up Spectron

I attempted to install Spectron using the following command: npm install --save-dev spectron However, I encountered the following error message: npm ERR! Windows_NT 6.1.7601 npm ERR! argv "C:\Program Files\nodejs\node.exe" "C:\P ...

Can I simultaneously utilize submit and ajax functions?

As I work on CRUD for our website, the approach we are taking involves using submit. However, there are occasions where I need to pass data from a JS file to my controller (I am using Codeigniter). I am now considering whether it is common practice to do ...

Overlapping problem with setInterval

I am currently working on a project that requires the use of both setInterval and setTimeout. I am using setTimeout with dynamic delays passed to it. While elements are not timed out, I have implemented setInterval to print out numbers. Here is the code ...

Creating a React component that allows for pagination using data fetched from a

I have a Spring Boot endpoint that retrieves and lists items from a database: @RequestMapping(method = RequestMethod.GET, value = "/task", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<?> processTask(@Valid TaskSearchP ...

JavaScript's Blob to Base64 conversion using FileReader is failing to produce any output

In my typescript application, I am utilizing the FileReader to convert a blob into a base64 image for display within the template. adaptResultToBase64(res: Blob): string { let imageToDisplay : string | ArrayBuffer | null = ''; const re ...

Interactive jQuery Popup character map

Is there a jQuery plugin available for a popup 'character map'? I have a text field and want users to easily insert special characters, like the TinyMCE plugin. ...

When sending an ajax request with HTML data as the payload

While working on an MVC program, I encountered an issue when trying to send data from a TextArea to the controller upon a button click. Everything worked fine until I had HTML-based data inside the TextArea. Here's a snippet of the HTML code: <te ...

Ways to have a React Component trigger a function with each state update

Using this specific component, the getDisplay function is triggered on every update like normal. When the <div> element is clicked, it becomes hidden: class Example extends React.Component { constructor(props) { super(props); thi ...

Could someone help me understand this JavaScript code where a function takes an object as a formal parameter?

Within a Vue component's methods, I came across the following code snippet defining a function: methods: { onEditorChange({ editor, html, text }) { console.log('editor change!', editor, html, text) this.content = html ...

Utilizing Nested JSON for Stacked Highcharts Implementation

I've been researching extensively about nested json for Highcharts stacked percentage column, but I haven't been able to get the desired output. Below is the code that I have tried, but unfortunately it's not producing the expected result. ...

Changing the Position of HTML Scripts in React

I am facing an issue where I need to relocate an external script within a specific section of my page. However, I am unable to access the CSS attributes associated with this item. It seems that it is displayed as an iFrame, making it difficult to modify th ...