three.js(四):react + three.js

绘制多个立方体

1.搭建react+ts 项目

bash 复制代码
npx create-react-app basics-demo --template typescript

2.安装three依赖

bash 复制代码
npm install three @types/three --save

3.安装路由

bash 复制代码
npm install react-router@6 react-router-dom@6

4.用路由组件包裹APP。

  • index.tsx
ts 复制代码
import { BrowserRouter } from "react-router-dom";
import { createRoot } from "react-dom/client";
import App from "./App";

const container = document.getElementById("root") as HTMLElement;
const root = createRoot(container);
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

5.构建项目页面

  • src/view/Basics.tsx
ts 复制代码
import React from "react";

const Basics: React.FC = (): JSX.Element => {
  return (
    <nav style={{ width: "60%", margin: "auto" }}>
      <h2>three.js 基础示例</h2>
    </nav>
  );
};

export default Basics;
  • src/view/RenderStructure.tsx
ts 复制代码
import React from "react";

const RenderStructure: React.FC = (): JSX.Element => {
  return <div>RenderStructure 渲染结构</div>;
};

export default RenderStructure;

6.用useRoutes hook 搭建路由。

  • App.tsx
ts 复制代码
import React from "react";
import { useRoutes } from "react-router-dom";
import "./App.css";
import Basics from "./view/Basics";
import RenderStructure from "./view/RenderStructure";

const App: React.FC = (): JSX.Element => {
  const routing = useRoutes([
    {
      path: "/",
      element: <Basics />,
    },
    {
      path: "RenderStructure",
      element: <RenderStructure />,
  ]);
  return <>{routing}</>;
};

export default App;

7.建立导航栏

  • src/view/Basics.tsx
ts 复制代码
import React from "react";
import { Link } from "react-router-dom";

const Basics: React.FC = (): JSX.Element => {
  return (
    <nav style={{ width: "60%", margin: "auto" }}>
      <h2>three.js 基础示例</h2>
      <ul>
        <li>
        	<Link to="/RenderStructure">RenderStructure 渲染结构</Link>
        </li>
      </ul>
    </nav>
  );
};
export default Basics;

8.在RenderStructure.tsx 页面导入立方体

  • src/view/RenderStructure.tsx
ts 复制代码
import React, { useRef, useEffect } from "react";
import { BoxGeometry, DirectionalLight, Mesh, MeshNormalMaterial, MeshPhongMaterial, PerspectiveCamera, Scene, WebGLRenderer } from "three";

const { innerWidth, innerHeight } = window;

const scene = new Scene();
const camera = new PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 1000);
camera.position.z = 5;

const renderer = new WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);

const geometry = new BoxGeometry();
const material = new MeshNormalMaterial();
const cube = new Mesh(geometry, material);
scene.add(cube);

function animate() {
  requestAnimationFrame(animate);
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
}

const RenderStructure: React.FC = (): JSX.Element => {
  const divRef = useRef<HTMLDivElement>(null);
  useEffect(() => {
    const { current } = divRef;
    if (current) {
      current.innerHTML = "";
      current.append(renderer.domElement);
      animate();
    }
    
  }, []);
  return <div ref={divRef}></div>;
};

export default RenderStructure;
  • 在上面的代码中,没有直接建立 ,而是在WebGLRenderer 对象的实例化方法里建立的,在其源码可以找到相关逻辑:
js 复制代码
function WebGLRenderer( parameters = {} ) {
	const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement()
	......
  this.domElement = _canvas;
  ......
}
  • 通过WebGLRenderer 对象建立了canvas后,再在react的函数组件的useEffect hook 中,将canvas 添加到div 中。
js 复制代码
const RenderStructure: React.FC = (): JSX.Element => {
  const divRef = useRef<HTMLDivElement>(null);
  useEffect(() => {
    const { current } = divRef;
    current && current.append(renderer.domElement);
    animate();
  }, []);
  return <div ref={divRef}></div>;
};
  • 当前这个立方体的材质是MeshNormalMaterial,并不受光照影响

9.给立方体换个MeshPhongMaterial 材质,再添加光源

ts 复制代码
const geometry = new BoxGeometry();
const material = new MeshPhongMaterial({ color: 0x44aa88 });
const cube = new Mesh(geometry, material);
scene.add(cube);

const color = 0xffffff;
const intensity = 1;
const light = new DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
  • 当前的渲染结构如下:
  • 效果如下:

10. 添加两个立方体,几何体和材质可被多个Mesh 对象共享

ts 复制代码
const geometry = new BoxGeometry();
const material = new MeshPhongMaterial({ color: 0x44aa88 });

const cubes = [-2, 0, 2].map((num) => makeInstance(num));
scene.add(...cubes);

function makeInstance(x: number) {
  const cube = new Mesh(geometry, material);
  cube.position.x = x;
  return cube;
}

function animate() {
  requestAnimationFrame(animate);

  cubes.forEach((cube) => {
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
  });

  renderer.render(scene, camera);
}
  • 当前的渲染结构如下:
  • 效果如下:
相关推荐
贵州数擎科技有限公司10 小时前
机械战警 Threejs实现
webgl·three.js
前端若水12 小时前
自定义消息组件:图片、文件附件与图表
前端·人工智能·react.js·typescript
放下华子我只抽RuiKe512 小时前
React 从入门到生产(七):性能优化实战
前端·javascript·人工智能·react.js·性能优化·前端框架·github
李燚13 小时前
ReAct 循环的 50 行 Go 实现,逐行拆解
javascript·人工智能·react.js·golang·aigc·agent
光影少年13 小时前
react自定义Hook 写法、规则(只能在组件/自定义Hook内调用)
前端·react.js·掘金·金石计划
暗不需求13 小时前
玩转 React Hooks:从基础到实战,逐行解析带你彻底掌握
前端·react.js·面试
放下华子我只抽RuiKe514 小时前
React 从入门到生产(六):路由与导航
前端·人工智能·深度学习·react.js·前端框架·html·claude code
前端若水1 天前
会话管理:创建、切换、删除对话历史
前端·人工智能·python·react.js
放下华子我只抽RuiKe51 天前
React 从入门到生产(四):自定义 Hook
前端·javascript·人工智能·深度学习·react.js·自然语言处理·前端框架