Unable to move in a Three.js scene?

Hello everyone!

I currently have this script here: Nabil - Photos THREE JS

… but I would like to have the same movement capability (via the MacBook trackpad) as this website: SOOT

Here is my current script:

<script>

// Variables globales
let camera, scene, renderer;
let images = [];
let frustum;
let cameraViewProjectionMatrix;
const IMAGE_LOAD_DISTANCE = 50;
let imageBoundingBox;
let minZoom = 0, maxZoom = 50; // Valeurs initiales, peuvent être ajustées dynamiquement
let targetZPosition = 30;

// Initialisation Three.js
function initThreeJS() {
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xf5f5f5);

    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = targetZPosition;

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    const canvasContainer = document.getElementById('canvas-container');
    if (canvasContainer) { // Vérification pour éviter une erreur si l'élément n'existe pas
        canvasContainer.appendChild(renderer.domElement);
    } else {
        console.error("Élément 'canvas-container' non trouvé.");
    }

    frustum = new THREE.Frustum();
    cameraViewProjectionMatrix = new THREE.Matrix4();
}

// Gestionnaire de chargement d'images (amélioré)
function loadImage(url, index, callback) {
    const loader = new THREE.TextureLoader();
    loader.load(url, texture => callback(texture, index), undefined, error => console.error(`Erreur de chargement de ${url}:`, error));
}

// Charge les images et les ajoute à la scène
function loadImages(imageUrls, scaleFactor) {
    const loadingManager = new THREE.LoadingManager();
    loadingManager.onProgress = (url, itemsLoaded, itemsTotal) => {
        const percent = (itemsLoaded / itemsTotal * 100).toFixed(0);
        const loadingIndicator = document.getElementById('loading-indicator') || createLoadingIndicator();
        loadingIndicator.textContent = `Chargement : ${percent}%`;
        if (itemsLoaded === itemsTotal) {
            setTimeout(() => (loadingIndicator.style.display = 'none'), 1000);
        }
    };

    imageUrls.forEach((url, index) => {
        loadImage(url, index, (texture, index) => {
            const width = texture.image.width * scaleFactor;
            const height = texture.image.height * scaleFactor;
            const geometry = new THREE.PlaneGeometry(width / 100, height / 100);
            const material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide, transparent: true, opacity: 1 });
            const mesh = new THREE.Mesh(geometry, material);
            mesh.position.x = (Math.random() - 0.5) * 80;
            mesh.position.y = (Math.random() - 0.5) * 40;
            mesh.position.z = (Math.random() - 0.5) * 10;
            mesh.userData.url = url;
            mesh.userData.highResLoaded = false;
            images.push(mesh);
            scene.add(mesh);
            if (index === imageUrls.length - 1) {
                calculateImageBounds();
            }
        });
    });
}

// Crée l'indicateur de chargement
function createLoadingIndicator() {
    const loadingIndicator = document.createElement('div');
    loadingIndicator.id = 'loading-indicator';
    loadingIndicator.style.cssText = `
        position: fixed;
        top: 20px;
        right: 20px;
        background: rgba(0,0,0,0.7);
        color: white;
        padding: 10px;
        border-radius: 5px;
        z-index: 1000;
    `;
    document.body.appendChild(loadingIndicator);
    return loadingIndicator;
}

// Calcule les limites de la boîte englobante des images
function calculateImageBounds() {
    imageBoundingBox = new THREE.Box3();
    images.forEach(mesh => imageBoundingBox.expandByObject(mesh));
    const size = new THREE.Vector3();
    imageBoundingBox.getSize(size);
    const diagonal = size.length();
    maxZoom = Math.max(50, diagonal * 2);
    camera.position.z = maxZoom * 0.5;
}

// Gestionnaire d'événements
function addEventListeners() {
    const canvas = renderer.domElement;
    let isDragging = false;
    let mouseDownPosition = null;
    let offsetX, offsetY;


    // Gestion du déplacement de la caméra avec la souris
    canvas.addEventListener('mousedown', (e) => {
        isDragging = true;
        mouseDownPosition = { x: e.clientX, y: e.clientY };
        e.preventDefault();
    });

    canvas.addEventListener('mousemove', (e) => {
        if (!isDragging) return;
        const deltaX = e.clientX - mouseDownPosition.x;
        const deltaY = e.clientY - mouseDownPosition.y;
        camera.position.x -= deltaX * 0.02;
        camera.position.y += deltaY * 0.02;
        mouseDownPosition = { x: e.clientX, y: e.clientY };
        e.preventDefault();
    });

    canvas.addEventListener('mouseup', () => { isDragging = false; });


    canvas.addEventListener('wheel', onWheel, { passive: false });
    canvas.addEventListener('click', onClick);


    // Gestion des événements tactiles (à réactiver si besoin)
    // ...
}

// Gestionnaire de l'événement wheel
function onWheel(event) {
    event.preventDefault();
    const zoomSpeed = 0.1;
    targetZPosition += event.deltaY * zoomSpeed;
    targetZPosition = Math.min(Math.max(targetZPosition, minZoom), maxZoom);
    camera.position.z = targetZPosition;
}

// Gestionnaire de clics sur les images
function onClick(event) {
    const raycaster = new THREE.Raycaster();
    const mouse = new THREE.Vector2((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1);
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(images);
    if (intersects.length > 0) {
        openImageSlider(intersects[0].object.userData.url);
    }
}

// Fonction pour ouvrir la boîte à lumière
function openImageSlider(imageUrl) {
    // ... (votre code existant) ...
}

// Gestionnaire de redimensionnement de la fenêtre
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    calculateImageBounds();
}

// Boucle d'animation
function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}

// Fonction d'initialisation principale
function init() {
    initThreeJS();
    const cmsImages = document.querySelectorAll('[data-img="person"]');
    const imageUrls = Array.from(cmsImages).map(img => img.src);
    const scaleFactor = 0.23;
    loadImages(imageUrls, scaleFactor);
    addEventListeners();
    animate();
}

document.addEventListener('DOMContentLoaded', init);
</script>

Can someone help me? Please.

https://threejs.org/docs/#examples/en/controls/OrbitControls

https://threejs.org/examples/#misc_controls_orbit

thanks for your message, but it’s not really the same way of moving around OrbitControl… Any idea how to move the camera with two fingers without having to click/drag?

Try using pointerup and pointerdown instead of mousedown and mouseup events. Maybe also check if there are any other pointer related events.

As for a test on your Mac, maybe try one of my viewers like GLTF Viewer to see what you get with touch controls. You should be able to paste the following link into the viewer’s URL text box and hit the Load button to load the model and start navigation:

https://github.com/KhronosGroup/glTF-Sample-Models/blob/d7a3cc8e51d7c573771ae77a57f16b0662a905c6/2.0/DamagedHelmet/glTF-Binary/DamagedHelmet.glb

There are a few configuration options for orbitcontrols that might enable the behavior you need:

https://threejs.org/docs/#examples/en/controls/OrbitControls

https://threejs.org/docs/#examples/en/controls/OrbitControls.mouseButtons

https://threejs.org/docs/#examples/en/controls/OrbitControls.touches

controls.touches = {
	ONE: THREE.TOUCH.ROTATE,
	TWO: THREE.TOUCH.DOLLY_PAN
}