在本章节中,我们将深入学习如何在React+Three.js应用程序中创建Three.js场景和相机。我们将了解如何设置场景以容纳3D元素,并在React组件中控制相机的视角。此外,我们还将处理React组件大小变化,并确保相机适应新的视口尺寸。
# 1. 创建Three.js场景(Scene)和相机(Camera)
在Three.js中,场景(Scene)是3D场景中所有对象的容器。我们将把所有的几何体、光源和相机都添加到场景中,然后由Three.js引擎来渲染它们。
我们已经在前面的章节中创建了一个简单的Three.js场景,现在让我们将其封装到一个独立的ThreeScene
组件中,并添加一些注释来说明代码的功能。
import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';
const ThreeScene = () => {
// 使用useRef来存储Three.js场景、相机和渲染器
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(() => {
// 在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;
// 使用requestAnimationFrame来循环渲染场景
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();
};
}, []);
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
现在,我们已经将Three.js场景封装到了ThreeScene
组件中,并在useEffect
钩子中进行了创建和清理的操作。我们还在组件中使用useRef
钩子来存储了场景、相机和渲染器等元素,这样我们就能在整个组件的生命周期中访问它们。
# 2. 在React中控制相机视角
Three.js相机(Camera)控制着我们在3D场景中看到的视角。通过调整相机的位置和朝向,我们可以改变我们在场景中的观察点。
在前面的章节中,我们在ThreeScene
组件中创建了一个透视相机(PerspectiveCamera)。现在,让我们添加一些交互功能来控制相机视角。
import React, { useRef, useEffect, useState } 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);
// 使用useState来跟踪相机的位置
const [cameraPosition, setCameraPosition] = useState({ x: 0, y: 0, z: 5 });
useEffect(() => {
// ...
// 在useEffect中创建Three.js场景和相机
// ...
// 使用useState中的相机位置来设置相机的位置
camera.current.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);
// ...
// 使用鼠标滚轮事件来控制相机的缩放
const handleWheel = (event) => {
event.preventDefault();
// 获取鼠标滚轮的delta值,并根据其方向调整相机的z位置
setCameraPosition((prevPosition) => ({
...prevPosition,
z: prevPosition.z + event.deltaY * 0.1,
}));
};
// 将鼠标滚轮事件监听器添加到窗口
window.addEventListener('wheel', handleWheel);
// 在组件卸载时,移除事件监听器
return () => {
window.removeEventListener('wheel', handleWheel);
};
}, [cameraPosition]);
// ...
// 其他代码(省略)
// ...
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
在上述代码中,我们使用useState
钩子来跟踪相机的位置,这样我们可以根据用户的交互来动态地更新相机的位置。
在useEffect
钩子中,我们使用鼠标滚轮事件来控制相机的缩放。当用户滚动鼠标滚轮时,我们根据滚动的方向调整相机的z位置,从而实现相机的缩放效果。
通过这样的交互
功能,我们可以更灵活地控制相机视角,让用户更好地观察3D场景中的元素。
# 3. 处理React组件大小变化和相机适应
在一个React应用中,组件的大小可能会因为窗口大小的变化而改变。当组件的大小变化时,我们需要相应地调整Three.js场景和相机,以确保场景适应新的视口尺寸。
让我们来添加相应的代码来处理组件大小变化和相机适应:
import React, { useRef, useEffect, useState } from 'react';
import * as THREE from 'three';
const ThreeScene = () => {
// ...
const handleResize = () => {
const width = window.innerWidth;
const height = window.innerHeight;
// 调整渲染器和相机的尺寸
camera.current.aspect = width / height;
camera.current.updateProjectionMatrix();
renderer.current.setSize(width, height);
};
useEffect(() => {
// ...
// 使用window的resize事件来监听窗口大小的变化
window.addEventListener('resize', handleResize);
// 在组件卸载时,移除resize事件监听器
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
在上述代码中,我们使用handleResize
函数来处理组件大小变化。在useEffect
钩子中,我们添加了一个事件监听器,来监听窗口的resize事件。当窗口大小发生变化时,我们调用handleResize
函数来更新渲染器和相机的尺寸,从而适应新的视口大小。
通过这样的处理,我们确保了Three.js场景和相机能够适应不同的屏幕尺寸,使我们的React+Three.js应用更具响应性和交互性。
在下一章节中,我们将学习如何在React中创建更复杂的几何体和材质,以及如何添加光源来增强场景的效果。敬请期待!