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)
}
// ...
}