第二章节:React和Three.js集成

7/17/2023 ReactThree.js

在上一章节中,我们已经成功创建了一个简单的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;
1
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;
1
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场景,并且可以适应窗口大小的变化。

React和Three.js集成

编辑时间: 7/17/2023, 10:30:56 AM