Home > other >  How can I access pure threejs elements with react without react-three-fiber?
How can I access pure threejs elements with react without react-three-fiber?

Time:04-05

Basically, I want to access my 3D model element as well as want to control the camera and other things like mesh, animation etc from another component. I'm using pure threeJs and react for this. I've added pure threejs code in the componentDidMount part of the react App class and I want to control suppose the camera part from componentDidMount from another component named CollapseButton. How can I access those camera,scene,materials from CollapseButton? Also when I click a button from CollapseButton I want to do a specific task with the threeJs part declared in the componentDidMount.

In short: From CollapseButton I want to click a button and do a specific task on the pure threeJs part declared in ComponentDidMount. Click a button defined in CollapseButton> Call a function or do something on componentDidmount/threejs part

Here is my App.js:

// ... App.js
import React from "react";
import ReactDOM from "react-dom";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import './App.css';
import CollapseButton from './CollapseButton'
class App extends React.Component {
  componentDidMount() {
    var renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    this.mount.appendChild(renderer.domElement);

    const sizes = {
      width: window.innerWidth,
      height: window.innerHeight,
    };

    //Creating scene
    var scene = new THREE.Scene();

    //creating camera
    var camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    );
    camera.position.x = 100;
    camera.position.y = 20;
    camera.position.z = 2;
    camera.lookAt(0, -10, 0);

    window.addEventListener("resize", () => {
      // Update sizes
      sizes.width = window.innerWidth;
      sizes.height = window.innerHeight;

      // Update camera
      camera.aspect = sizes.width / sizes.height;
      camera.updateProjectionMatrix();

      // Update renderer
      renderer.setSize(sizes.width, sizes.height);
      renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    });

    //loader
    let mesh;
    const gltfLoader = new GLTFLoader();
    gltfLoader.load("LeftBankUnitBoxes.glb", handle_load);
    const texture = new THREE.TextureLoader();
    const grass = texture.load("/textures/Grass.PNG");
    function handle_load(gltf) {
      mesh = gltf.scene;
      mesh.traverse((child) => {
        // if (child.material && child.material.name === "M_UnitBox_349") {
        //     child.material.map=grass

        //     // console.log(child.material)

        //   }
        if (child.material) {
          child.material.map = grass;
          // child.material.metalness=0.8
          // child.material.roughness=0.2
          // child.material = new THREE.MeshStandardMaterial({
          //   map: grass,
          //   metalness:1,
          //   roughness:10,
          //   metalnessMap:asphalt,
          //   envMap:cubeTextureLoader
          //   // transparent:true,
          //   // opacity: 0.2,
          // });
          // console.log(child.material)
        }
      });

      mesh.position.y = -50;
      scene.add(mesh);
    }

    function call(){
      console.log("Calling log")
    }

    //creating controls
    const orbit = new OrbitControls(camera, renderer.domElement);
    orbit.maxPolarAngle = Math.PI / 2;
    // orbit.autoRotate = true;
    orbit.update();

    //creating lights

    var Dirlight = new THREE.DirectionalLight("#ffffff", 1);
    var ambient = new THREE.AmbientLight("#ffffff", 1);
    Dirlight.position.set(-50, 20, 0).normalize();

    Dirlight.castShadow = true;
    scene.add(Dirlight);

    scene.add(ambient);

    //animate
    var animate = function () {
      requestAnimationFrame(animate);

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

  render() {
    return <div ref={(ref) => (this.mount = ref)} />;
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <>
    <App />
    <div className="Collapse">
      <CollapseButton call={call}/>
    </div>
  </>,
  rootElement
);
export default App;

Here is my CollapseButton:

import React, { useState } from 'react'
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import { Button } from '@mui/material';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';

export default function Collapse({call}) {
    const [icon,setIcon]=useState(false)

    const show=()=>{
        //handleRender()
        call()
        setIcon(!icon)
    }
  return (
    <>
        <Button style={{maxWidth: '30px', maxHeight: '30px', minWidth: '30px', minHeight: '30px'}} onClick={show}>
            {icon?<ArrowBackIosIcon style={{color:'white',fontSize:24}}/>:<ArrowForwardIosIcon  style={{color:'white',fontSize:24,fontWeight:'bold'}} />}
        </Button>
        
    </>
  )
}

I tried to pass a function from ComponentDidMount to CollapseButton which may called when I click a button from CollapseButton but couldn't find any way to pass the function.

CodePudding user response:

You can store a reference to your threeJS component in a React ref and expose it to your collapse button as a prop (or by a React context if you plan to use it in more than one component)

As a prop:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.objectRef = React.createRef();
  }

  componentDidMount() {
    // ...three.js code
    this.objectRef.current = myObject;
  }

  render() {
    return (
     <>
       <div ref={(ref) => (this.mount = ref)} />
       <div className="Collapse">
         <CollapseButton call={call} object={objectRef} />
       </div>
     </>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <App />,
  rootElement
);
export default App;
export default function Collapse({ call, object }) {
  // ...

    const show=()=>{
        const myObject = object.current; // Get the object
        // Perform the task here

        call()
        setIcon(!icon)
    }

  // ...
}

Using context:

const ThreeContext = React.createContext({});

class App extends React.Component {
  constructor(props) {
    super(props);
    this.objectRef = React.createRef();
  }

  componentDidMount() {
    // ...three.js code
    this.objectRef.current = myObject;
  }

  render() {
    return (
     <ThreeContext.Provider value={{ object: this.objectRef }}>
       <div ref={(ref) => (this.mount = ref)} />
       <div className="Collapse">
         <CollapseButton call={call} />
       </div>
     </ThreeContext.Provider>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <App />,
  rootElement
);
export default App;
export default function Collapse({ call }) {
  // ...

    const show=()=>{
        const myObject = this.context.object?.current; // Get the object
        // Perform the task here

        call()
        setIcon(!icon)
    }

  // ...
}
  • Related