Why is this code so slow in mobile?

this is a code for a image gallery i am working on by modifying the code of periodic table given in three js examples.This is so slow in mobile and when i click on an image it takes so long to load in mobile. See sample here

Abi's Space

<!DOCTYPE html>
<html>

<head>
    <title>Abi's Clicks</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <link type="text/css" rel="stylesheet" href="main.css">
    <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=DM+Sans&display=swap" rel="stylesheet">

    <script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/three@0.151.2/build/three.module.js",
      "three/addons/": "https://unpkg.com/three@0.151.2/examples/jsm/"
    }
  }
</script>
    <style>
        * {
            font-family: 'DM Sans', sans-serif;
        }

        body {
            background-color: #000;
            margin: 0px;
            overflow: hidden;
        }

        .element {
            width: 300px;
            height: 180px;
            background-size: 100% 100%;
            box-shadow: rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px;
            border-radius: 10px;

        }

        #info {
            background-color: rgb(0, 0, 0);
            width: 100%;
            height: 100%;
            position: fixed;
            top: 0px;
            z-index: 100;
            display: none;
        }

        #chosen_ {
            height: 80vh;
            align-items: center;
            margin: auto;
            justify-content: center;
            color: white;
            border-radius: 1rem;

        }

        #remove_ {
            position: absolute;
            top: 10px;
            right: 30px;
            z-index: 100;
            border-radius: 0.2rem;
            background-color: red;
            color: white;
            border: none;
            padding: 0.5rem;
            font-size: 1rem;
            margin: auto;

        }

        .name {
            width: 100%;
            padding: 5px;
            text-align: center;
            color: #000000;
            font-family: Monospace;
            font-size: 13px;
            font-weight: bold;
            line-height: 1.5;
            z-index: 100;
            background-color: #fff;
        }

        a {
            color: rgb(253, 0, 0);
            text-decoration: none;
        }

        h1 {
            display: inline-block;
        }
    </style>
</head>

<body>
    <div class="name">
        <h1>Abi's Clicks</h1>
        <a href="https://threejs.org/examples/?q=peri#css3d_periodictable">[Credits]</a>
    </div>
    <div id="info">
        <button onclick="document.getElementById('info').style.display = 'none';" id="remove_">Close</button>
        <img src="dummy" alt="dummy" id="chosen_" loading="lazy" />
    </div>

    <div id="container"></div>
    <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

    <script type="importmap">
			{
				"imports": {
					"three": "../build/three.module.js",
					"three/addons/": "./jsm/"
				}
			}
		</script>
    <script type="module">

        import * as THREE from 'three';

        import TWEEN from 'three/addons/libs/tween.module.js';
        import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
        import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js';

        const table = [
            { i: "file1.jpg", x: 1, y: 1 },
            { i: "21.JPG", x: 18, y: 1 },
            { i: "22.JPG", x: 1, y: 2 },
            { i: "23.JPG", x: 2, y: 2 },
            { i: "24.JPG", x: 13, y: 2 },
            { i: "25.JPG", x: 14, y: 2 },
            { i: "26.JPG", x: 15, y: 2 },
            { i: "27.JPG", x: 16, y: 2 },
            { i: "28.JPG", x: 17, y: 2 },
            { i: "29.JPG", x: 18, y: 2 },
            { i: "30.JPG", x: 1, y: 3 },
            { i: "31.JPG", x: 2, y: 3 },
            { i: "32.JPG", x: 13, y: 3 },
            { i: "33.JPG", x: 14, y: 3 },
            { i: "34.JPG", x: 15, y: 3 },
            { i: "35.JPG", x: 16, y: 3 },
            { i: "36.JPG", x: 17, y: 3 },
            { i: "37.JPG", x: 18, y: 3 },
            { i: "38.JPG", x: 1, y: 4 },
            { i: "39.JPG", x: 2, y: 4 },
            { i: "40.JPG", x: 3, y: 4 },
            { i: "41.JPG", x: 4, y: 4 },
            { i: "42.JPG", x: 5, y: 4 },
            { i: "file2.jpg", x: 6, y: 4 },
            { i: "file3.jpg", x: 7, y: 4 },
            { i: "file4.jpg", x: 8, y: 4 },
            { i: "file5.jpg", x: 9, y: 4 },
            { i: "file6.jpg", x: 10, y: 4 }
        ];

        let camera, scene, renderer;
        let controls;

        const objects = [];
        const targets = { table: [], sphere: [], helix: [], grid: [] };
        init();
        animate();

        function init() {

            camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000);
            camera.position.z = 3000;

            scene = new THREE.Scene();
            document.getElementById('info').style.display = 'block';
            for (let i = 0; i < table.length; i++) {

                const element = document.createElement('div');
                element.style.backgroundImage = 'url(images/' + (table[i].i) + ')';                
                element.className = 'element';
                element.addEventListener('mousedown', function () {
                    document.getElementById('chosen_').src = 'images/' + (table[i].i);
                    document.getElementById('info').style.display = 'flex';

                });
                element.addEventListener('touchstart', function () {
                    document.getElementById('chosen_').src = 'images/' + (table[i].i);
                    document.getElementById('info').style.display = 'flex';

                });


                const objectCSS = new CSS3DObject(element);
                objectCSS.position.x = Math.random() * 4000 - 2000;
                objectCSS.position.y = Math.random() * 4000 - 2000;
                objectCSS.position.z = Math.random() * 4000 - 2000;
                scene.add(objectCSS);
                objects.push(objectCSS);
                const object = new THREE.Object3D();
                object.position.x = (table[i].x * 140) - 1330;
                object.position.y = - (table[i].y * 180) + 990;
                targets.table.push(object);

            }
            document.getElementById('info').style.display = 'none';

            const vector = new THREE.Vector3();

            for (let i = 0, l = objects.length; i < l; i++) {

                const phi = Math.acos(- 1 + (2 * i) / l);
                const theta = Math.sqrt(l * Math.PI) * phi;

                const object = new THREE.Object3D();

                object.position.setFromSphericalCoords(800, phi, theta);

                vector.copy(object.position).multiplyScalar(2);

                object.lookAt(vector);

                targets.sphere.push(object);

            }

            renderer = new CSS3DRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);

            document.getElementById('container').appendChild(renderer.domElement);

            controls = new TrackballControls(camera, renderer.domElement);
            controls.minDistance = 500;
            controls.maxDistance = 6000;
            controls.addEventListener('change', render);

            transform(targets.sphere, 6000);

            window.addEventListener('resize', onWindowResize);

        }

        function transform(targets, duration) {

            TWEEN.removeAll();

            for (let i = 0; i < objects.length; i++) {

                const object = objects[i];
                const target = targets[i];

                new TWEEN.Tween(object.position)
                    .to({ x: target.position.x, y: target.position.y, z: target.position.z }, Math.random() * duration + duration)
                    .easing(TWEEN.Easing.Exponential.InOut)
                    .start();

                new TWEEN.Tween(object.rotation)
                    .to({ x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, Math.random() * duration + duration)
                    .easing(TWEEN.Easing.Exponential.InOut)
                    .start();

            }

            new TWEEN.Tween(this)
                .to({}, duration * 2)
                .onUpdate(render)
                .start();

        }

        function onWindowResize() {

            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();

            renderer.setSize(window.innerWidth, window.innerHeight);

            render();

        }

        function animate() {

            requestAnimationFrame(animate);
            //camera.position.x = Math.cos(Date.now() * 0.0001) * 3000;
            TWEEN.update();
            controls.update();

        }

        function render() {
            renderer.render(scene, camera);

        }

    </script>
</body>

</html>

Please fix your formatting, also consider putting your code into a live example you can also use jsfiddle.net, codepen.io or codesandbox.io.

I’m unsure why this would be slow, but then again I don’t know much about CSSRenderer internals. I do however have one suggestion:

Place requestAnimationFrame(animate); at the bottom of your animate function. This is a very common mistake that is included in a lot of example code that is being thrown around. The reason for this is that requestAnimationFrame schedules a callback to be executed before the browser performs the next repaint. If you schedule the next frame at the beginning of the function, the browser may execute the callback before all of the processing is complete, resulting in janky or incomplete animations.

By scheduling the next frame at the end of the function, you ensure that all processing for the current frame is complete before scheduling the next one, resulting in smoother and more consistent animations.

Not sure if it’ll do anything for your case in particular though :thinking:

1 Like