Pinch Zoom Functionality in Three.js

I’m looking to incorporate Pinch Zoom functionality into my three.js panorama viewer.

After some research, it appears that TrackBallControls.js might have this feature built in?

I attempted to implement it, but encountered an error:

'Uncaught TypeError: undefined is not a function'

specifically on the line:

var controls = new THREE.TrackballControls( camera );

Since I am dynamically loading all Three.js scripts using Sid.js due to the nature of my system, could that be why it’s unable to locate THREE.TrackballControls?

Furthermore, is TrackBallControls the right solution for pinch and zoom functionality on mobile devices within a Three.js environment?

Answer №1

The TrackBallControls.js library does include touch zoom functionality, as shown in the code snippet below. However, this library is not integrated into the core Three.js package; it's only available in the example projects. You can access the source code here.

function touchstart( event ) {

    if ( _this.enabled === false ) return;

    switch ( event.touches.length ) {

        case 1:
            _state = STATE.TOUCH_ROTATE;
            _rotateStart.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
            _rotateEnd.copy( _rotateStart );
            break;

        case 2:
            _state = STATE.TOUCH_ZOOM_PAN;
            var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
            var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
            _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );

            var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
            var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
            _panStart.copy( getMouseOnScreen( x, y ) );
            _panEnd.copy( _panStart );
            break;

        default:
            _state = STATE.NONE;

    }
    _this.dispatchEvent( startEvent );


}

function touchmove( event ) {

    if ( _this.enabled === false ) return;

    event.preventDefault();
    event.stopPropagation();

    switch ( event.touches.length ) {

        case 1:
            _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
            break;

        case 2:
            var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
            var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
            _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );

            var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
            var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
            _panEnd.copy( getMouseOnScreen( x, y ) );
            break;

        default:
            _state = STATE.NONE;

    }

}

function touchend( event ) {

    if ( _this.enabled === false ) return;

    switch ( event.touches.length ) {

        case 1:
            _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
            _rotateStart.copy( _rotateEnd );
            break;

        case 2:
            _touchZoomDistanceStart = _touchZoomDistanceEnd = 0;

            var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
            var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
            _panEnd.copy( getMouseOnScreen( x, y ) );
            _panStart.copy( _panEnd );
            break;

    }

    _state = STATE.NONE;
    _this.dispatchEvent( endEvent );

}

Answer №2

If someone requires the code, I've made some adjustments to it

function handleTouchStart(event) {

    if (event.touches.length == 1) {

        console.log('1');
        event.preventDefault();

        initPointerX = event.touches[0].pageX;
        initPointerY = event.touches[0].pageY;

        initLon = lon;
        initLat = lat;

        checkHotspotClick();

    }

    if (event.touches.length == 2) {

        console.log('2');
        _state = STATE.TOUCH_ZOOM_PAN;
        var dx = event.touches[0].pageX - event.touches[1].pageX;
        var dy = event.touches[0].pageY - event.touches[1].pageY;
        _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt(dx * dx + dy * dy);

    }

}

function handleTouchMove(event) {

    if (event.touches.length == 1) {

        event.preventDefault();

        lon = (initPointerX - event.touches[0].pageX) * 0.1 + initLon;
        lat = (event.touches[0].pageY - initPointerY) * 0.1 + initLat;

    }

    if (event.touches.length == 2) {

            var dx = event.touches[0].pageX - event.touches[1].pageX;
            var dy = event.touches[0].pageY - event.touches[1].pageY;
            _touchZoomDistanceEnd = Math.sqrt(dx * dx + dy * dy);

        var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
        _touchZoomDistanceStart = _touchZoomDistanceEnd;
        adjustZoom(camera.fov * factor);

    }

}

function handleTouchEnd(event) {

            _touchZoomDistanceStart = _touchZoomDistanceEnd = 0;

}

function adjustZoom(fov){

    camera.fov = fov;

    if(camera.fov < 30) camera.fov = 30;
    if(camera.fov > 100) camera.fov = 100;

    camera.updateProjectionMatrix();

}

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

What is the most effective way to use checkboxes to apply multiple filters to an array of objects?

Let me simplify the description. Within the App component, I fetch data from a JSON file and store it in the flightsList array. My goal is to filter this array based on checkboxes selected in the FlightOptions and Airlines components. The issue that I&a ...

What is the process for displaying a PHP array in HTML5 audio and video players?

I am currently working with two PHP arrays. The first array, array "a," contains strings that represent paths for MP3 files on the server. The second array, array "b," contains strings representing paths for MP4 files. For example: $test = 'a.mp3&ap ...

Strange behavior of shadows within Three.js

I've been working on creating a mini solar system but encountered an interesting issue. I want all the planets to cast and receive shadows from each other, but it seems like the shadow casting depends on the order of instancing. Here is the code for t ...

Activate the "order evaluation" trigger on the checkout page in Woocommerce

I have implemented the Woocommerce Advanced Shipping plugin created by Jeroen Sormani for managing shipping methods, along with the WooCommerce Pay for Payment plugin developed by Karolína Vyskočilová to add a fixed €5 fee to the "cash on delivery" pa ...

Is it possible to include numbers and commas in JQuery Validation?

I have implemented a jQuery validation plugin to validate the fields of a form. One specific requirement is to validate a field to only allow commas and numbers. Below is the HTML code snippet: <input type="text" placeholder="Number of Employees" requ ...

Need help with writing code in Angular for setting intervals and clearing intervals?

I am working on the functionality to display a loader gif and data accordingly in Angular. I have tried using plain JavaScript setInterval code but it doesn't work for $scope.showLoader=true and $scope.showResult=true. The console.log('found the ...

The issue of React state not being properly updated in Jest tests, even though it functions correctly in

Clicking the button increments the value by the amount specified in the input. The functionality works properly within the app, but when attempting to use Jest and first simulate a fireEvent.change() on the input field to set the value to 10, followed by a ...

Enhancing Numbers with JavaScript

What I'm Looking for: I have 5 counters on my webpage, each starting at 0 and counting upwards to different set values at varying speeds. For example, if I input the values of 10, 25, 1500, 400, and 500 in my variables, I want all counters to reach t ...

React Apollo Error - When using refetchQueries, data does not re-render in one component but does so in another. Surprisingly, it works when the refetchQueries is in the same

Within my application, there is a Main Component that displays a list of todos. Additionally, the Sidebar Component showcases various products as illustrated below ...

Leveraging Express.js alongside websockets

I am currently working on an application that is built on Expressjs and Angularjs. I am looking to incorporate a few two-way calls in addition to the existing HTTP calls. While I have gone through some websocket chat tutorials, none of them seem to be inte ...

Steps for uploading an item to an API using an Excel document

I'm facing an issue where I am trying to send a large object along with an Excel file to an API. However, only my photo is being recognized and the object is not sent, resulting in an [object Object] error. I understand that this error occurs due to i ...

The API for /api/addproducts has been resolved without a response being sent, potentially causing delays in processing requests

A response was not sent for the /api/addproducts API, which can lead to delays in processing requests. Below is the code for the add product API that I have been working on: I've debugged it using the console, but I'm still struggling to find t ...

Troubleshooting Selenium JS: Challenges with Locating Elements Across Pages

I'm facing a challenge in accessing elements on pages other than the main page in my electron app. Although there are multiple elements that load when the app starts, I can only interact with elements on the initial page. I believe the issue lies in h ...

Converting DateTime objects into JSON format for use in AJAX calls

When utilizing my AJAX method, it returns a view model that is serialized as a data structure using JavaScriptSerializer().Serialize(). Among this data are several nullable DateTime? properties. I recently discovered that these dates appear in JavaScript ...

Navigating Through Secondary Navigation with Ease

In my React project, I am developing a mega-menu styled dropdown navigation. As a functional component, I am utilizing the useState hook to manage the state and control the display of sub-navigation items. The functionality of the dropdown is operational, ...

Sending data from TextBoxFor to controller with @Ajax.ActionLink in MVC

I’ve gone through numerous questions dealing with the same issue, yet none of them seem to solve my problem (or I’m completely missing the point). As the title suggests, I am attempting to transfer the value from a TextBoxFor to my controller using an ...

The issue lies within the representation of the vector's projection

In my current project using release 116, I encountered an issue with the camera position and vector projection. Initially, after changing the camera position and attempting to get the projection of the vector, I would receive an incorrect value. However, ...

Guide on Incorporating Coffeescript into the Node.js Blueprint Framework

Have you checked out Skeleton (https://github.com/dstroot/skeleton) yet? It appears to be a robust framework for node.js, offering all the middleware you need. However, it seems to lack support for coffee script. How can we incorporate it into our project? ...

The search for the view in the directory "/views" was unsuccessful in Express 4.0

I've been working on a project using Express 4.0 and the Express3-handlebars libraries for NodeJS. Below is the setup: app.set('views', path.join(__dirname, 'views/')); app.engine('hbs', hbs({defaultLayout: 'main&a ...

Is it possible to access a service within the config function or access a provider from inside a service?

Need help configuring angular bootstrap tooltip (uibTooltip) to be disabled on mobile devices with angular-bowser for device detection. One possible solution is: isMobile = _bowser.mobile $uibTooltipProvider.options = { trigger: isMobile ? "none" : "mous ...