OrbitControls can't disable zoom on mobile

Hi, I added some code inside my three.js script to be specific for mobile devices, however I cant disable zoom on orbitcontrols, also the rotation is way too fast. Can someone explain this?

Edit: Didn’t realize I was recording w. music, sorry:))

         if (window.innerWidth < 767) {
        camera.position.z = 1.95;
        pointOfIntersection = false;
        const controls = new OrbitControls( camera, renderer.domElement );
    controls.enabled = true; // Ensure that controls are enabled
    controls.enableZoom = false; // Disable zoom
    controls.update();

    }

You’re gonna have to show more code than that. Could be anything.

Here you go.

1 Like

You’re creating the orbitcontrols inside the renderloop so… creating a new one 60 times a second :smiley: I’m surprised your machine didn’t catch fire.

instead of this:


          function animate() {
          requestAnimationFrame(animate);

          // Remove mouse move event listener when window width meets a certain condition
          renderer.render(scene, camera);

          if (window.innerWidth < 767) { camera.position.z=1.95; pointOfIntersection=false; const controls=new OrbitControls(camera, renderer.domElement); controls.enableRotation=true; // Ensure that controls are enabled controls.enableZoom=false; // Disable zoom controls.autoRotate=false; } } animate();

you want:

const controls=new OrbitControls(camera, renderer.domElement); controls.enableRotation=true; // Ensure that controls are enabled controls.enableZoom=false; // Disable zoom controls.autoRotate=false; }

function animate() {
          requestAnimationFrame(animate);

          // Remove mouse move event listener when window width meets a certain condition
          if (window.innerWidth < 767) { camera.position.z=1.95; pointOfIntersection=false; }

          renderer.render(scene, camera);
 } 
animate();

The lack of formatting is hiding issues, and possibly creating them.

That window.innerWidth check looks suspect… not sure what that’s about.

Put your js in a .js file and just include it from the html too.
Here’s your formatted/fixed code.

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <style> 
              canvas {
                  cursor: pointer;
        width: 100% !important;
        height: 100% !important;
        z-index: 9999999999; //whyyy
      }
      
      
    </style>
    <script type="importmap">
    {
        "imports": {
            "three": "https://cdn.skypack.dev/three@0.129.0/build/three.module.js",
            "three/addons/": "https://cdn.skypack.dev/three@0.129.0/examples/jsm/",
        }
    }</script>
  </head>
  <body>
    <div id="container3D"></div>
    <script type="module" src = "./yourcode.js"></script>
  </body>
</html>


yourcode.js

import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

//scene
const scene = new THREE.Scene();
//camera
const camera = new THREE.PerspectiveCamera(
    70,
    window.innerWidth / window.innerHeight,
    0.1,
    10
);

camera.position.z = 1.27;
camera.position.y = 0.75;

//renderer
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById("container3D").appendChild(renderer.domElement);
window.addEventListener("resize", () => {
    const newWidth = window.innerWidth;
    const newHeight = window.innerHeight;

    renderer.setSize(newWidth, newHeight);
});

let skeleton = null; // Declare the skeleton variable

//loader + set scale
const loader = new GLTFLoader();
let model = null;

loader.load("/test.glb", (gltf) => {
    model = gltf.scene;
    scene.add(model);
    model.scale.set(0.5, 0.5, 0.5); // Adjust scale for the first model

    model.traverse((child) => {
        if (child.isMesh) {
            child.material = new THREE.MeshStandardMaterial({
                color: 0xffffff,
            }); // Adjust properties as needed
        }

        //adding the rig (I think, found this online)
        skeleton = new THREE.SkeletonHelper(model); //add a skeleton helper to ensure everythings set up correct
        skeleton.visible = false;
        scene.add(skeleton);

        skeleton.bones.forEach((bone, index) => {
            console.log(`Bone ${index}:`, bone);
        });
    });
});

const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x080820); // Sky color, ground color, intensity
scene.add(hemisphereLight);

var mouse = new THREE.Vector2();

let plane = new THREE.Plane(new THREE.Vector3(0, 0, 2), -1);
let raycaster = new THREE.Raycaster();
let pointOfIntersection = new THREE.Vector3();

window.addEventListener("mousemove", function (e) {
    mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);
    raycaster.ray.intersectPlane(plane, pointOfIntersection);

    skeleton.bones[2].lookAt(pointOfIntersection);
});

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableRotation = true; // Ensure that controls are enabled
controls.enableZoom = false; // Disable zoom
controls.autoRotate = false;


function animate() {
    requestAnimationFrame(animate);

    controls.update();

    // Remove mouse move event listener when window width meets a certain condition
    renderer.render(scene, camera);

    if (window.innerWidth < 767) { //Don't know what this is.. you probably want 'blur'/'focus' event for this?
        camera.position.z = 1.95;
        pointOfIntersection = false;
    }
}

animate();

I want to enable Orbitcontrols for mobile devices only, not tablets or computers. That’s why I put it inside the window.innerWidth tag, your solution unfortunately also enables on PC’s/Tablet, which is what I wanted to avoid.

The reason I included the js inside the html is because I am using a web builder and so I have to include everything in one HTML widget :slight_smile:

PS. My computer almost did catch fire! I didn’t realize I placed it inside the loop lmao

1 Like

Using window width is not a good check. There are many mobile devices that have higher resolution than my 4-years-old computer.

Maybe some library (like https://detector.js.org/) might provide a better identification?


PS. I do not advocate for detector-js, I have never used it, it is just the first one showing up in my search

1 Like

@mattias_98 maybe try including a mobile check to determine the state of controls, here is an example included in the code @manthrax posted:

import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

//scene
const scene = new THREE.Scene();
//camera
const camera = new THREE.PerspectiveCamera(
    70,
    window.innerWidth / window.innerHeight,
    0.1,
    10
);

camera.position.z = 1.27;
camera.position.y = 0.75;

//renderer
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById("container3D").appendChild(renderer.domElement);
window.addEventListener("resize", () => {
    const newWidth = window.innerWidth;
    const newHeight = window.innerHeight;

    renderer.setSize(newWidth, newHeight);
});

let skeleton = null; // Declare the skeleton variable

//loader + set scale
const loader = new GLTFLoader();
let model = null;

loader.load("/test.glb", (gltf) => {
    model = gltf.scene;
    scene.add(model);
    model.scale.set(0.5, 0.5, 0.5); // Adjust scale for the first model

    model.traverse((child) => {
        if (child.isMesh) {
            child.material = new THREE.MeshStandardMaterial({
                color: 0xffffff,
            }); // Adjust properties as needed
        }

        //adding the rig (I think, found this online)
        skeleton = new THREE.SkeletonHelper(model); //add a skeleton helper to ensure everythings set up correct
        skeleton.visible = false;
        scene.add(skeleton);

        skeleton.bones.forEach((bone, index) => {
            console.log(`Bone ${index}:`, bone);
        });
    });
});

const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x080820); // Sky color, ground color, intensity
scene.add(hemisphereLight);

var mouse = new THREE.Vector2();

let plane = new THREE.Plane(new THREE.Vector3(0, 0, 2), -1);
let raycaster = new THREE.Raycaster();
let pointOfIntersection = new THREE.Vector3();

window.addEventListener("mousemove", function (e) {
    mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);
    raycaster.ray.intersectPlane(plane, pointOfIntersection);

    skeleton.bones[2].lookAt(pointOfIntersection);
});

const isMobile = (/iPad|iPhone|iPod/.test( navigator.platform ))
         || (/Android|webOS|iPhone|iPad|iPod|CriOS|BlackBerry|IEMobile|Opera Mini/i.test( navigator.userAgent ))
          || (navigator.maxTouchPoints !== undefined && navigator.maxTouchPoints > 2 && /MacIntel/.test( navigator.platform ));

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableRotation = true;

if (isMobile === true) {
    controls.enableZoom = false; // Disable zoom
    controls.autoRotate = false;
    camera.position.z = 1.95;
    pointOfIntersection = false;
}

function animate() {
    requestAnimationFrame(animate);

    controls.update();

    renderer.render(scene, camera);
}

animate();

This would remove the window width check from the animate loop.
You can modify isMobile conditions to whatever devices you would treat as mobile.

1 Like

@mattias_98 you can also try adjusting any other OrbitControls settings, including rotation speed:

          controls.zoomToCursor = true;
          controls.zoomSpeed = 0.4;
          controls.rotateSpeed = 0.4;
          controls.keyPanSpeed = 0.4; 
          controls.panSpeed = 0.4;
          //controls.enableDamping = true;
          //controls.dampingFactor = 0.3;
          //controls.enableKeys = false;
          //controls.enableZoom = true;
          //controls.maxPolarAngle = 2.2;
          //controls.minPolarAngle = 1.1;
          //controls.minDistance = 2;
          //controls.maxDistance = 500;