如何在没有 react-three-fiber 的情况下通过 react 访问纯 threejs 元素?
How can I access pure threejs elements with react without react-three-fiber?
基本上,我想访问我的 3D 模型元素,并想从另一个组件控制相机和其他东西,如网格、动画等。我正在使用纯 threeJs 并为此做出反应。
我在 react App class 的 componentDidMount 部分添加了纯 threejs 代码,我想控制来自另一个名为 CollapseButton 的组件的 componentDidMount 的相机部分。如何从 CollapseButton 访问那些相机、场景、材料?此外,当我单击 CollapseButton 中的按钮时,我想使用 componentDidMount 中声明的 threeJs 部分执行特定任务。
简而言之:从 CollapseButton 我想点击一个按钮并在 ComponentDidMount 中声明的纯 threeJs 部分上执行特定任务。单击 CollapseButton 中定义的按钮 > 调用函数或在 componentDidmount/threejs 部分
上执行某些操作
这是我的 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;
这是我的折叠按钮:
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>
</>
)
}
我试图将一个函数从 ComponentDidMount 传递到 CollapseButton,当我从 CollapseButton 中单击一个按钮时可能会调用它,但找不到任何方法来传递该函数。
如果您打算在多个组件中使用它,您可以将对 threeJS 组件的引用存储在一个 React ref and expose it to your collapse button as a prop (or by a React context 中)
作为道具:
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)
}
// ...
}
使用上下文:
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)
}
// ...
}
您可以将 three.js
相关代码移动到不同的文件中并在需要的地方导入
T3.js
// Creating renderer
const renderer = new THREE.WebGLRenderer();
// Creating scene
const scene = new THREE.Scene();
// Creating camera
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const animate = function () {
// requestAnimationFrame(this.animate);
renderer.render(scene, camera);
this.orbit.update();
};
export default {
renderer,
scene,
camera,
texture: new THREE.TextureLoader(),
gltfLoader: new GLTFLoader(),
gltfHandler: function (gltf) {
console.log(this);
const grass = this.texture.load('/textures/Grass.PNG');
let 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);
},
orbit: new OrbitControls(camera, renderer.domElement),
sizes: {
width: window.innerWidth,
height: window.innerHeight,
},
init: function () {
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.camera.position.x = 100;
this.camera.position.y = 20;
this.camera.position.z = 2;
this.camera.lookAt(0, -10, 0);
window.addEventListener('resize', () => {
// Update sizes
this.sizes.width = window.innerWidth;
this.sizes.height = window.innerHeight;
// Update camera
this.camera.aspect = this.sizes.width / this.sizes.height;
this.camera.updateProjectionMatrix();
// Update renderer
this.renderer.setSize(this.sizes.width, this.sizes.height);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
//loader
this.gltfLoader.load('LeftBankUnitBoxes.glb', this.gltfHandler);
//creating controls
this.orbit.maxPolarAngle = Math.PI / 2;
// orbit.autoRotate = true;
this.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;
this.scene.add(Dirlight);
this.scene.add(ambient);
},
animate,
call: function () {
console.log('Calling log');
},
};
App.js
import React from 'react';
import './style.css';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import { Button } from '@mui/material';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import T3 from './T3'
class App extends React.Component {
componentDidMount() {
T3.init();
this.mount.appendChild(T3.renderer.domElement);
T3.animate();
}
render() {
return <div ref={(ref) => (this.mount = ref)} />;
}
}
Collapse.js
import React, { useState } from 'react'
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import { Button } from '@mui/material';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import T3 from './T3'
function Collapse({ call }) {
const [icon, setIcon] = useState(false);
const show = () => {
//handleRender()
T3.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>
</>
);
}
基本上,我想访问我的 3D 模型元素,并想从另一个组件控制相机和其他东西,如网格、动画等。我正在使用纯 threeJs 并为此做出反应。 我在 react App class 的 componentDidMount 部分添加了纯 threejs 代码,我想控制来自另一个名为 CollapseButton 的组件的 componentDidMount 的相机部分。如何从 CollapseButton 访问那些相机、场景、材料?此外,当我单击 CollapseButton 中的按钮时,我想使用 componentDidMount 中声明的 threeJs 部分执行特定任务。
简而言之:从 CollapseButton 我想点击一个按钮并在 ComponentDidMount 中声明的纯 threeJs 部分上执行特定任务。单击 CollapseButton 中定义的按钮 > 调用函数或在 componentDidmount/threejs 部分
上执行某些操作这是我的 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;
这是我的折叠按钮:
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>
</>
)
}
我试图将一个函数从 ComponentDidMount 传递到 CollapseButton,当我从 CollapseButton 中单击一个按钮时可能会调用它,但找不到任何方法来传递该函数。
如果您打算在多个组件中使用它,您可以将对 threeJS 组件的引用存储在一个 React ref and expose it to your collapse button as a prop (or by a React context 中)
作为道具:
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)
}
// ...
}
使用上下文:
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)
}
// ...
}
您可以将 three.js
相关代码移动到不同的文件中并在需要的地方导入
T3.js
// Creating renderer
const renderer = new THREE.WebGLRenderer();
// Creating scene
const scene = new THREE.Scene();
// Creating camera
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const animate = function () {
// requestAnimationFrame(this.animate);
renderer.render(scene, camera);
this.orbit.update();
};
export default {
renderer,
scene,
camera,
texture: new THREE.TextureLoader(),
gltfLoader: new GLTFLoader(),
gltfHandler: function (gltf) {
console.log(this);
const grass = this.texture.load('/textures/Grass.PNG');
let 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);
},
orbit: new OrbitControls(camera, renderer.domElement),
sizes: {
width: window.innerWidth,
height: window.innerHeight,
},
init: function () {
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.camera.position.x = 100;
this.camera.position.y = 20;
this.camera.position.z = 2;
this.camera.lookAt(0, -10, 0);
window.addEventListener('resize', () => {
// Update sizes
this.sizes.width = window.innerWidth;
this.sizes.height = window.innerHeight;
// Update camera
this.camera.aspect = this.sizes.width / this.sizes.height;
this.camera.updateProjectionMatrix();
// Update renderer
this.renderer.setSize(this.sizes.width, this.sizes.height);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
//loader
this.gltfLoader.load('LeftBankUnitBoxes.glb', this.gltfHandler);
//creating controls
this.orbit.maxPolarAngle = Math.PI / 2;
// orbit.autoRotate = true;
this.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;
this.scene.add(Dirlight);
this.scene.add(ambient);
},
animate,
call: function () {
console.log('Calling log');
},
};
App.js
import React from 'react';
import './style.css';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import { Button } from '@mui/material';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import T3 from './T3'
class App extends React.Component {
componentDidMount() {
T3.init();
this.mount.appendChild(T3.renderer.domElement);
T3.animate();
}
render() {
return <div ref={(ref) => (this.mount = ref)} />;
}
}
Collapse.js
import React, { useState } from 'react'
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import { Button } from '@mui/material';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import T3 from './T3'
function Collapse({ call }) {
const [icon, setIcon] = useState(false);
const show = () => {
//handleRender()
T3.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>
</>
);
}