Animations issues when on Meta Quest 2 VR Mode (Three JS)

  Kiến thức lập trình

I have a React project using three.js, and I want to provide a seamless experience for users without page breaks. To achieve this, I avoided using page routing because it would require re-entering VR mode each time a page is switched. Instead, I’ve included three animations in a single file, with animations transitioning automatically based on a timer.

import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { VRButton } from 'three/examples/jsm/webxr/VRButton';

// Função para configurar a cena, câmera e renderizador
const initializeScene = (container) => {
  const scene = new THREE.Scene();
  scene.background = new THREE.Color(0x0000ff);

  const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.set(0, 1.5, 3);

  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.xr.enabled = true;
  container.appendChild(renderer.domElement);
  document.body.appendChild(VRButton.createButton(renderer));

  const controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.25;
  controls.enableZoom = true;

  const environment = new RoomEnvironment();
  const pmremGenerator = new THREE.PMREMGenerator(renderer);
  scene.environment = pmremGenerator.fromScene(environment).texture;

  return { scene, camera, renderer, controls };
};

// Funções auxiliares para animações de página
const createCubePage1 = (scene, renderer, camera) => {
  const cubeGeometry1 = new THREE.BoxGeometry();
  const textureLoader = new THREE.TextureLoader();
  const texture = textureLoader.load('utils/moovz.jpg');
  const cubeMaterial1 = new THREE.MeshBasicMaterial({ map: texture });
  const cube1 = new THREE.Mesh(cubeGeometry1, cubeMaterial1);

  cube1.position.set(0, -5, -5);
  scene.add(cube1);

  let startTime;
  let emergingDuration = 3000;
  let floatAmplitude = 0.2;
  let isEmergingComplete = false;

  const startAnimation = () => {
    startTime = Date.now();
    const animateCube1 = () => {
      const elapsedTime = Date.now() - startTime;
      if (elapsedTime < emergingDuration) {
        const progress = elapsedTime / emergingDuration;
        cube1.position.y = -5 + (7 * progress);
      } else {
        if (!isEmergingComplete) {
          cube1.position.y = 1.9;
          isEmergingComplete = true;
        }
        cube1.position.y = 2 + Math.sin(Date.now() * 0.001) * floatAmplitude;
      }
      cube1.rotation.x += 0.001;
      cube1.rotation.y += 0.001;
      renderer.render(scene, camera);
      requestAnimationFrame(animateCube1);
    };
    requestAnimationFrame(animateCube1);
  };
  setTimeout(startAnimation, 10000);
  return 20000; // Tempo de redirecionamento
};

const createModelPage2 = (scene, renderer, camera) => {
  const gltfLoader = new GLTFLoader();
  gltfLoader.load('utils/marca 3d eixo x.glb', (gltf) => {
    const model = gltf.scene;
    scene.add(model);
    model.position.set(-20, 0, -15);
    const targetPosition = new THREE.Vector3(-8, 0, -15);
    const duration = 3000;
    const startTime = Date.now();

    const animateModel = () => {
      const elapsedTime = Date.now() - startTime;
      const progress = Math.min(elapsedTime / duration, 1);
      model.position.lerpVectors(new THREE.Vector3(-20, 0, -15), targetPosition, progress);
      renderer.render(scene, camera);
      if (progress < 1) {
        requestAnimationFrame(animateModel);
      }
    };
    requestAnimationFrame(animateModel);
  }, undefined, (error) => {
    console.error('Erro ao carregar o modelo GLB:', error);
  });
  return 10000; // Tempo de redirecionamento
};

const createCubePage3 = (scene, renderer, camera) => {
  camera.position.set(0, 1.5, 5);
  const cubeGeometry3 = new THREE.BoxGeometry();
  const cubeMaterial3 = new THREE.MeshStandardMaterial({ color: 0x0000ff });
  const cube3 = new THREE.Mesh(cubeGeometry3, cubeMaterial3);
  cube3.position.set(0, 1, -2);
  scene.add(cube3);

  const animateCube3 = () => {
    cube3.rotation.x += 0.01;
    cube3.rotation.y += 0.01;
    renderer.render(scene, camera);
    requestAnimationFrame(animateCube3);
  };
  requestAnimationFrame(animateCube3);
  return null; // Sem redirecionamento
};

// Componente principal
const Page = () => {
  const containerRef = useRef(null);
  const sceneRef = useRef(null);
  const cameraRef = useRef(null);
  const rendererRef = useRef(null);
  const [currentPage, setCurrentPage] = useState(1);
  const redirectTimeoutRef = useRef(null);

  useEffect(() => {
    if (containerRef.current) {
      const { scene, camera, renderer, controls } = initializeScene(containerRef.current);

      sceneRef.current = scene;
      cameraRef.current = camera;
      rendererRef.current = renderer;

      const onWindowResize = () => {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      };
      window.addEventListener('resize', onWindowResize);

      const animate = () => {
        if (renderer.xr.isPresenting) {
          camera.position.set(0, 1.5, 3);
          controls.enabled = false;
        } else {
          controls.update();
        }
        renderer.render(scene, camera);
      };
      renderer.setAnimationLoop(animate);

      return () => {
        window.removeEventListener('resize', onWindowResize);
        clearTimeout(redirectTimeoutRef.current);
        if (renderer.domElement.parentNode) {
          renderer.domElement.parentNode.removeChild(renderer.domElement);
        }
      };
    }
  }, []);

  useEffect(() => {
    const scene = sceneRef.current;
    const renderer = rendererRef.current;
    const camera = cameraRef.current;

    if (scene && camera && renderer) {
      // Limpa o conteúdo anterior
      while (scene.children.length > 0) {
        scene.remove(scene.children[0]);
      }

      let redirectTime;
      switch (currentPage) {
        case 1:
          redirectTime = createCubePage1(scene, renderer, camera);
          break;
        case 2:
          redirectTime = createModelPage2(scene, renderer, camera);
          break;
        case 3:
          redirectTime = createCubePage3(scene, renderer, camera);
          break;
        default:
          break;
      }

      if (redirectTime) {
        redirectTimeoutRef.current = setTimeout(() => {
          setCurrentPage((prevPage) => prevPage + 1); // Muda para a próxima página
        }, redirectTime);
      }
    }
  }, [currentPage]);

  return <div ref={containerRef} style={{ width: '100vw', height: '100vh' }}></div>;
};

export default Page;    

I tested this setup using Mozilla Firefox with the WebXR extension, and it worked perfectly. However, when I tested it on a Meta Quest 2, the animation doesn’t work when entering VR mode in the browser.

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website

LEAVE A COMMENT