在上一章节中,我们已经成功创建了一个简单的React+Three.js场景。在本章节中,我们将更深入地学习如何将React和Three.js集成,以便更好地管理和控制我们的3D场景。
# 1. 在React组件中集成Three.js场景
在上一章节中,我们创建了一个名为ThreeScene
的React组件,其中包含了一个简单的Three.js场景。让我们继续在这个组件中添加更多的Three.js元素和交互。
import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';
const ThreeScene = () => {
const sceneRef = useRef(null);
useEffect(() => {
// 在这里创建Three.js场景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
sceneRef.current.appendChild(renderer.domElement);
// 创建一个简单的红色立方体
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 创建一个绿色平面作为地面
const planeGeometry = new THREE.PlaneGeometry(5, 5);
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2;
plane.position.y = -1;
scene.add(plane);
// 将相机定位到远离立方体并渲染场景
camera.position.z = 5;
const animate = () => {
requestAnimationFrame(animate);
// 使立方体旋转
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
};
animate();
// 当组件卸载时,清理Three.js场景和渲染器
return () => {
scene.remove(cube);
scene.remove(plane);
geometry.dispose();
material.dispose();
planeGeometry.dispose();
planeMaterial.dispose();
renderer.dispose();
};
}, []);
return <div ref={sceneRef} />;
};
export default ThreeScene;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
在上述代码中,我们添加了一个绿色的平面作为地面,并调整了相机的位置,以便更好地查看立方体和地面。请注意,我们在useEffect
钩子中创建了Three.js场景和渲染器,并在组件卸载时进行了清理操作,以避免内存泄漏。
# 2. Three.js渲染器和React的渲染结合
在React中,我们使用Virtual DOM来管理页面渲染。而在Three.js中,我们使用WebGL渲染器直接在画布上进行绘制。为了正确结合React和Three.js的渲染,我们需要确保React的渲染不干扰到Three.js场景的渲染。
在上一章节中,我们将Three.js的渲染器挂载到<div>
元素上。这样,Three.js将在该元素上绘制3D场景,而React将在其他元素上绘制其他内容。这种分离确保了React和Three.js之间的渲染不会相互干扰,同时保持了良好的性能。
# 3. 在React中管理Three.js场景的生命周期
在React中,组件的生命周期方法是非常有用的。在我们的ThreeScene
组件中,我们已经在useEffect
钩子中创建和清理了Three.js场景。但是,如果组件被重新渲染,我们也需要相应地更新Three.js场景。
React提供了一些生命周期方法,我们可以利用它们来处理组件的重新渲染和更新Three.js场景。
让我们来看一下在ThreeScene
组件中如何使用生命周期方法:
import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';
const ThreeScene = () => {
const sceneRef = useRef(null);
const scene = useRef(null);
const camera = useRef(null);
const renderer = useRef(null);
const cube = useRef(null);
const plane = useRef(null);
useEffect(() => {
// 在这里创建Three.js场景
scene.current = new THREE.Scene();
camera.current = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer.current = new THREE.WebGLRenderer();
renderer.current.setSize(window.innerWidth, window.innerHeight);
sceneRef.current.appendChild(renderer.current.domElement);
// 创建一个简单的红色立方体
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
cube.current = new THREE.Mesh(geometry, material);
scene.current.add(cube.current);
// 创建一个绿色平面作为地面
const planeGeometry = new THREE.PlaneGeometry(5, 5);
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide });
plane.current = new THREE.Mesh(planeGeometry, planeMaterial);
plane.current.rotation.x = -Math.PI / 2;
plane.current.position.y = -1;
scene.current.add(plane.current);
// 将相机定位到远离立方体并渲染场景
camera.current.position.z = 5;
const animate = () => {
requestAnimationFrame(animate);
// 使立方体旋转
cube.current.rotation.x += 0.01;
cube.current.rotation.y += 0.01;
renderer.current.render(scene.current, camera.current);
};
animate();
// 当组件卸载时,清理Three.js场景和渲染器
return () => {
scene.current.remove(cube.current);
scene.current.remove(plane.current);
geometry.dispose();
material.dispose();
planeGeometry.dispose();
planeMaterial.dispose();
renderer.current.dispose();
};
}, []);
// 当窗口大小发生变化时,更新相机和渲染器的尺寸
useEffect(() => {
const handleResize = () => {
const width = window.innerWidth;
const height = window.innerHeight;
camera.current.aspect = width / height;
camera.current.updateProjectionMatrix();
renderer.current.setSize(width, height);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return <div ref={sceneRef} />;
};
export default ThreeScene;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
在上述代码中,我们使用了useRef
钩子来存储Three.js场景中的元素,以便在组件的整个生命周期中都能访问它们。我们还添加了另一个useEffect
钩子,以便在窗口大小发生变化时更新相机和渲染器的尺寸,以确保场景适应新的视口大小。
现在,我们的ThreeScene
组件更加健壮,能够更好地管理和控制Three.js场景,并且可以适应窗口大小的变化。