Press the mouse button to position the camera close to an element in Three.js

Trying to implement a click to zoom feature with Three.js, I have a canvas and an object loaded in it. When clicking, I want to position the camera near the point of intersection for a zoom effect.

Here is my current code, but it's not working as expected. The camera position changes on click, but it only partially works. Sometimes the camera is placed near the intersection point, sometimes not.

 onmousedown = function (event) {

                var raycaster = new THREE.Raycaster();
                var mouse = new THREE.Vector2();

                 event.preventDefault();
                mouse.x = (event.clientX / self.renderer.domElement.clientWidth) * 2 - 1;
                mouse.y = -(event.clientY / self.renderer.domElement.clientHeight) * 2 + 1;
                raycaster.setFromCamera(mouse, self.camera);
                var objects = [];
                for (var i = 0; i < self.scene.children.length; i++) {
                         if (self.scene.children[i] instanceof  THREE.Group) {
                        objects.push(self.scene.children[i]);
                    }
                }
                console.log(objects);
                var intersects = raycaster.intersectObjects( objects,true );
                console.log(intersects.length);
                if (intersects.length > 0) {
                            self.camera.up = new THREE.Vector3(0, 0, 1);
                    self.camera.lookAt(new THREE.Vector3(0, 0, 0));
                    self.camera.position.z = intersects[0].point.z * .9;
                    self.camera.position.x = intersects[0].point.x * .9;
                    self.camera.position.y = intersects[0].point.y * .9;



                }

            };

Using a global viewer object called self which contains camera, canvas, and various other objects.

The number 0.9 is simply a value used to position the camera near the intersection point.

The camera used is a PerspectiveCamera and the controls are TrackballControls.

new THREE.PerspectiveCamera(90, this.width / this.height, 1, 1000);

The objects loaded are from .obj or .dae files. The expected behavior is to click on any point of the object and have the camera move near that point. However, the camera sometimes doesn't move near the clicked point.

  • Does intersects[0] give the nearest intersection point, or the nearest in the direction of the camera?
  • Where am I going wrong in this implementation?

I am new to Three.js and just starting to learn. Please point out any mistakes or logical errors in my code.

Answer №1

Calculating the exact position can be a challenging task; it involves determining the distance between the camera and the intersection, then placing the camera at a specific distance along that segment while facing the intersection point.

Here is one way to approach it:

var distance = [desired camera-intersection distance]
var direction = camera.position.clone().sub(intersects[0].point).normalize().multiplyScalar(distance);
camera.position = intersects[0].point.clone().add(direction);
camera.lookAt(intersects[0].point);

Answer №2

If you take a moment to visit my fiddle here: http://jsfiddle.net/h5my29aL/

Conceptually, imagine your object as a celestial body, and your camera as a satellite orbiting around it. The key is to position the camera in a strategic orbit relative to your object. The 'Three' function includes a handy distanceTo feature that simplifies this process. While the example showcases a sphere, it can be applied to any mesh. Essentially, it calculates the distance from the center point to the designated vector3. In this scenario, the vector3 likely corresponds to the face position detected by a picker ray. Regardless, the camera's lookAt is aligned with the mesh, and a calculation determines the distance from the vertex to ensure a consistent altitude irrespective of the vertex or face's distance from the object center.

var point = THREE.GeometryUtils.randomPointsInGeometry( geometry, 1 );
    var altitude = 100;
    var rad = mesh.position.distanceTo( point[0] );
    var coeff = 1+ altitude/rad;
    camera.position.x = point[0].x * coeff;
    camera.position.y = point[0].y * coeff;
    camera.position.z = point[0].z * coeff;
    camera.lookAt(mesh.position);

Answer №3

I have managed to come close to achieving my desired result using a sample from Three.js library.

Check out the Three.js webgl_decals example

This is the code snippet of what I have accomplished:

function zoomCam(event) {

var point_mouse = new THREE.Vector2(),
    var point_x = null;
    var point_y = null;
            if (event.changedTouches) {

                point_x = event.changedTouches[ 0 ].pageX;
                point_y = event.changedTouches[ 0 ].pageY;
            } else {

                point_x = event.clientX;
                point_y = event.clientY;
            }

            point_mouse.x = (point_x / window.innerWidth) * 2 - 1;
            point_mouse.y = -(point_y / window.innerHeight) * 2 + 1;

            if (sceneObjects.length > 0) {

                var raycaster = new THREE.Raycaster();
                raycaster.setFromCamera(point_mouse, camera);
                var intersects = raycaster.intersectObjects(sceneObjects, true);
                if (intersects.length > 0) {
                    var p = intersects[ 0 ].point;
                var n = intersects[ 0 ].face.normal.clone();
                n.multiplyScalar(10);
                n.add(intersects[ 0 ].point);
                camera.position.copy(n);
                camera.lookAt(p);
                }
            }

There may be some minor issues due to formatting changes for this response. Please review the code before implementation.

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

Are there any conventional methods for modifying a map within an Aerospike list?

Attempting to modify an object in a list using this approach failed const { bins: data } = await client.get(key); // { array: [{ variable: 1 }, { variable: 2 }] } const { array } = await client.operate(key, [Aerospike.maps.put('array', 3).withCon ...

What could be causing my code to become unresponsive when using a for loop compared to a loop with a specific

After spending a solid 4 hours on it, I finally managed to figure it out. There were no errors popping up, so I resorted to using the debug feature which unfortunately didn't provide much insight. Without any error messages to guide me, I wasn't ...

Utilizing a JavaScript class within the document ready function

Having trouble with a countdown script wrapped as an object in a separate file. Struggling to setup multiple counters or objects due to the timeout function in the countdown class not finding the object that was set up within the document ready event. Wh ...

What is the best way to make a Dojo TitlePane overlap using CSS?

My dilemma involves a jsfiddle featuring two TitlePane widgets in the central pane's top right corner. Currently, clicking on the right TitlePane ("Switch Basemap") moves the left TitlePane ("Map Overlays") to the left. What I want is for the right Ti ...

"Troubleshooting the lack of functionality in nested ajax calls, click events, or event

Initially, a navigation click event was created: $('#inner-navigation li a') .on('click', function (e) { e.preventDefault(); AjaxNavUrl.checkURL(this.hash); }); This event triggers an ajax call and respo ...

Implementing Fullpage.js with persistent elements throughout slides

Can I keep certain elements fixed between slides? I found that the only way to accomplish this was by placing the text element outside of the slide div. <div class="section" id="section1"> <div class="intro"> <h1> ...

The TypeScript error reads: "An element is implicitly assigned the 'any' type because an expression of type 'any' cannot be used to index a specific type."

[Hey there!][1] Encountering this TypeScript error message: { "Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ 0: { image: string; title: string; text: string; }; 1: { ...

Encountering the issue "Error: _LoginPage.default is not a constructor"

This is the code I wrote: /// \<reference types = "cypress" /\> class LoginPage { visit() { cy.visit("https://ec2-35-179-99-242.eu-west-2.compute.amazonaws.com:2021/") } username(name) ...

JavaScript frame malfunction causing page to continuously refresh

Once, I encountered a situation where my blog would display a frame or plugin when accessed from a particular website that I pinged. Wanting to remove this frame after 30 seconds, I managed to do so. However, the code I used resulted in my blog continuousl ...

Ensuring the model accurately reflects the input's value attribute in AngularJS

I am in the process of creating a set of image "radio buttons" where only one can be selected per group. However, as a newcomer to Angular, I'm facing difficulties in maintaining the value and using it as my ng-model. Additionally, I am looking for a ...

Error in typescript: The property 'exact' is not found in the type 'IntrinsicAttributes & RouteProps'

While trying to set up private routing in Typescript, I encountered the following error. Can anyone provide assistance? Type '{ exact: true; render: (routerProps: RouterProps) => Element; }' is not compatible with type 'IntrinsicAttribu ...

Error 500: Issue with JQuery AJAX File Upload

Hey there, I'm facing an issue with doing a file upload using JQuery's AJAX feature as I keep getting the error 500. $(function() { $( 'form' ).submit ( function() { $.ajax({ type: &a ...

When I attempt to use document.execCommand("copy"), the line break does not get applied

I am currently using the following code to create a string and copy it. However, when I paste it as output, the line break is not being applied. function copyToClipboardShipto() { var $temp = $("<input>"); $("body").append($ ...

Is there a way to have the user input data into a Firebase database directly from a Vue.js component?

Any assistance for a beginner like me would be greatly appreciated. I am currently working with vuejs and firebase to write data into the database from a vue component. I have successfully implemented authentication and writing functionality, but now I wan ...

Having trouble with Bootstrap's "hidden-xs" class not working and struggling to create a sticky footer for small viewports

Trying to tackle the challenge of making my footer stick to the bottom of the page on smaller screens has been quite troublesome. As a temporary fix, I decided to experiment with hiding the div entirely until I figure out a proper solution. The HTML < ...

Vue component fails to trigger upon receiving the 'mouseleave' event

I am currently working on a navbar with dynamic component navigation, where hovering over a navbar-link should display the corresponding component and hiding it when the mouse leaves. Although the components are displayed correctly upon hover, they do not ...

Is there a way to repeatedly call a function without losing its previous outputs, without relying on a database? What alternative should I consider using instead?

I'm looking to create multiple tables using the same function in JavaScript. However, when I call the function again, the previous table disappears. I am new to coding in JavaScript and would appreciate any suggestions on how to handle this issue. I p ...

Is there a way to prevent my Pixlee Instagram feed from disappearing when I reload the page in NextJS?

Here is the code snippet I'm working with: import { useEffect } from "react"; const InstagramFeed = () => { useEffect(() => { window.PixleeAsyncInit = function () { Pixlee.init({ apiKey: "MYAPIKEYISHERE" }); ...

Is Proxy.apply() not functioning correctly on Node.js? I'm unsure if this is a bug or if I am implementing it incorrectly

Utilizing a Proxy object has been quite helpful for me. The getter and setter functions are working perfectly as expected. However, I have encountered an issue where the apply method is never invoked. var p = new Proxy({}, { /* getter */ get(t ...

Encountering a snag when trying to load JavaScript within an HTML document

I encountered an error while trying to load an HTML file in the JavaScript console of the Brave browser. The error message reads: require.js:5 Uncaught Error: Module name "constants.js" has not been loaded yet for context: _. Use require([]) https://requir ...