Search code examples
javascriptthree.js3ddrag-and-droporbitcontrols

Three JS: OrbitControls the pixel of picture jumps when dragging


I recently started to deal with 3d, I want to move around the scene, but when dragging, I see how the pixels in the pictures start jumping before the camera stops moving. This is clearly the wrong job

Why is this happening? And how to avoid it? My code: https://codepen.io/davedev13/pen/zYEyxRX

const data = {
    "objects": [
        {
            "img": "https://picsum.photos/400/500",
            "title": "Creative",
            "description": "lorem ipsum",
            "url": "/1",
            "position": {
                "left": "-500",
                "top": "500"
            }
        },
        {
            "img": "https://picsum.photos/400/500",
            "title": "Angles",
            "description": "lorem ipsum",
            "url": "/2",
            "position": {
                "left": "500",
                "top": "-500"
            }
        },
        {
            "img": "https://picsum.photos/400/500",
            "title": "Awwards",
            "description": "lorem ipsum",
            "url": "/3",
            "position": {
                "left": "500",
                "top": "0"
            }
        },
        {
            "img": "https://picsum.photos/400/500",
            "title": "Conexe",
            "description": "lorem ipsum",
            "url": "/4",
            "position": {
                "left": "0",
                "top": "500"
            }
        },
        {
            "img": "https://picsum.photos/400/500",
            "title": "Luxury",
            "description": "lorem ipsum",
            "url": "/5",
            "position": {
                "left": "-500",
                "top": "0"
            }
        },
        {
            "img": "https://picsum.photos/400/500",
            "title": "Develop",
            "description": "lorem ipsum",
            "url": "/6",
            "position": {
                "left": "0",
                "top": "-500"
            }
        },
        {
            "img": "https://picsum.photos/400/500",
            "title": "Desing",
            "description": "lorem ipsum",
            "url": "/7",
            "position": {
                "left": "500",
                "top": "500"
            }
        },
        {
            "img": "https://picsum.photos/400/500",
            "title": "Sociality",
            "description": "lorem ipsum",
            "url": "/8",
            "position": {
                "left": "-500",
                "top": "-500"
            }
        }
    ]
}

class Drag {
    drag = {
        width: 2560,
        height: 1440,
    };
    mouseOut = false;

    constructor(container) {
        this.container = container;
        let w = container.clientWidth;
        let h = container.clientHeight;
        let viewSize = h;
        let aspectRatio = w / h;

        this.viewport = {
            viewSize: viewSize,
            aspectRatio: aspectRatio,
            left: (-aspectRatio * viewSize) / 2,
            right: (aspectRatio * viewSize) / 2,
            top: viewSize / 2,
            bottom: -viewSize / 2,
            near: -10,
            far: 100
        }

        this.initScene();
    }

    initScene() {
        this.camera = new THREE.OrthographicCamera(
            this.viewport.left, this.viewport.right,
            this.viewport.top, this.viewport.bottom,
            this.viewport.near, this.viewport.far
        );

        this.scene = new THREE.Scene();

        this.renderer = new THREE.WebGLRenderer({
            // alpha: true // чтобы сцена была прозрачной
        });

        this.renderer.domElement.id = 'canvasGrid';
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.setClearColor(0xdcdcdc, 1);

        this.container.appendChild(this.renderer.domElement);

        // драг контейнер для сцены
        const geometry = new THREE.PlaneGeometry(this.drag.width, this.drag.height);
        const material = new THREE.MeshBasicMaterial({
            color: 0xdcdcdc,
        });
        this.drag.plane = new THREE.Mesh(geometry, material);

        // драг бокс по которому гранимац которого будет двигаться сцена при драге
        this.dragBox = new THREE.Box3().setFromObject(this.drag.plane);

        this.scene.add(this.drag.plane);

        this.sceneObjects();
        this.setControls();
        this.animate();

        window.addEventListener('mouseout', () => this.mouseOut = true, false);
        window.addEventListener('mouseover', () => {
            this.mouseOut = false;
            this.animate();
        }, false);
    }



    sceneObjects() {
        // instantiate a loader
        const loader = new THREE.TextureLoader();

        data.objects.map((item) => {
            const group = new THREE.Group();
            group.name = item.title;

            // load a resource
            loader.load(
                // resource URL
                item.img,
                // onLoad callback
                function (texture) {
                    const width = texture.image.naturalWidth * 0.5;
                    const height = texture.image.naturalHeight * 0.5;

                    // in this example we create the material when the texture is loaded
                    const geometry = new THREE.BoxGeometry(width, height, 0);
                    const material = new THREE.MeshBasicMaterial({map: texture});
                    const mesh = new THREE.Mesh(geometry, material);

                    group.add(mesh);
                    group.position.set(item.position.left, item.position.top, 0);
                },

                // onProgress callback currently not supported
                undefined,

                // onError callback
                function (err) {
                    console.error('An error happened.', err);
                }
            );

            this.scene.add(group);
        });

        const geometry1 = new THREE.BoxGeometry(550, 300, 0);
        const texture = new THREE.TextureLoader().load("https://miro.medium.com/max/1400/1*Ynit7J26tXLwyq-sB3AUug.png");
        console.log(texture)
        const material1 = new THREE.MeshBasicMaterial({map: texture, opacity: 0.3});
        const mesh = new THREE.Mesh(geometry1, material1);
        this.scene.add(mesh);
    }

    render() {
        // if (this.mouseDownPressed) {
        let x1 = this.camera.position.x + (this.camera.left / this.camera.zoom);
        let x1a = Math.max(x1, this.dragBox.min.x);
        let pos_x = x1a - (this.camera.left / this.camera.zoom);

        let x2 = pos_x + (this.camera.right / this.camera.zoom);
        let x2a = Math.min(x2, this.dragBox.max.x);
        pos_x = x2a - (this.camera.right / this.camera.zoom);

        let y1 = this.camera.position.y + (this.camera.bottom / this.camera.zoom);
        let y1a = Math.max(y1, this.dragBox.min.y);
        let pos_y = y1a - (this.camera.bottom / this.camera.zoom);

        let y2 = pos_y + (this.camera.top / this.camera.zoom);
        let y2a = Math.min(y2, this.dragBox.max.y);
        pos_y = y2a - (this.camera.top / this.camera.zoom);

        this.camera.position.set(pos_x, pos_y, this.camera.position.z);
        this.camera.lookAt(pos_x, pos_y, this.controls.target.z); // todo: what is it?
        this.controls.target.set(pos_x, pos_y, 0);
        this.controls.update();
        // }

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

    setControls() {
        // Need to be similar to what is in OrbitControls3Dpane.js constructor
        this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
        this.controls.mouseButtons = {
            LEFT: THREE.MOUSE.PAN,
            MIDDLE: THREE.MOUSE.DOLLY,
            RIGHT: THREE.MOUSE.PAN
        }
        //////////////////////////////////////
        // Set rotate related parameters
        //////////////////////////////////////
        // No rotation.
        this.controls.enableRotate = false;
        this.controls.minPolarAngle = Math.PI / 2;
        this.controls.maxPolarAngle = Math.PI / 2;
        // No orbit horizontally.
        this.controls.minAzimuthAngle = 0; // radians
        this.controls.maxAzimuthAngle = 0; // radians


        //////////////////////////////////////
        // Set zoom related parameters
        //////////////////////////////////////
        this.controls.enableZoom = true;
        this.controls.zoomSpeed = 0.9;
        this.controls.minDistance = this.camera.near;
        this.controls.maxDistance = this.camera.far;
        this.controls.minZoom = window.innerWidth > window.innerHeight ?
            window.innerWidth / this.drag.width : window.innerHeight / this.drag.height;
        this.controls.maxZoom = 1 + this.controls.minZoom;

        //////////////////////////////////////
        // Set pan related parameters
        //////////////////////////////////////
        this.controls.enablePan = true;
        this.controls.panSpeed = 0.6;
        this.controls.screenSpacePanning = true;
        this.controls.enableDamping = true;

        // this.renderer.domElement.addEventListener('mousedown', () => {
        //  this.mouseDownPressed = true;
        // }, false);

        // this.renderer.domElement.addEventListener('mouseup', () => {
        //  setTimeout(() => {
        //      this.mouseDownPressed = false;
        //  }, 700);
        // }, false);
    }

    animate() {
        this.render();

        if (!this.mouseOut) {
            requestAnimationFrame(this.animate.bind(this));
        }
    }
}

function init() {
    new Drag(document.querySelector('.canvas'));
}

init();
.canvas {
  width: 100vw;
  height: 100vh;
}
<!-- partial:index.partial.html -->
<div class="canvas"></div>
<!-- partial -->
  <script src='https://threejs.org/build/three.js'></script>
<script src='https://threejs.org/examples/js/controls/OrbitControls.js'></script><script  src="./script.js"></script>

I created a scene using "OrthographicCamera" and move this camera along with OrbitControls, perhaps this is not the best option in my case, if so - tell me how to do it correctly?


Solution

  • antialias: true resolved at WebGLRenderer

    with all the same settings, the scene immediately became smoother and the pictures do not bug

    that is, he lowered the performance and taxied out...