当前位置: 首页 > 工具软件 > Fiber.js > 使用案例 >

如何用React three fiber在React中使用three.js

单于浩邈
2023-12-01

介绍

react three-fiber(通常缩写为R3F)允许您在react应用程序中使用three.js为web浏览器创建和显示3D计算机图形。

如果你熟悉使用React,并且你想创建一个three.js的体验,那么标准的方法可能很难扩展。react three-fiber通过提供在react生态系统中工作的three.js约定来解决这个痛点。

一些使用react three-fiber创建的项目包括Let Girls Dream、Gucci 1955年的Horse Bit Bag和Gucci的24 Hour Ace。

在本文中,您将学习如何利用react-three-fiber,使用three.js将3D图形和动画添加到React应用程序中。

先决条件

本文假设您熟悉React(带钩子)和three.js。如果不熟悉,您可以快速入门React钩子和快速上手three.js。

注意:此项目的实时版本可在CodeSandbox上获得。出于本教程的目的,将强调在多个文件中进行代码拆分,以更好地说明如何使用组件。需要注意的是,这种方法并没有进行性能优化。

本教程假设您有一个React应用程序,它有three,react three-fiber,以及lodash和依赖项。

探索react-three-fiber

以下是为您的下一个项目考虑使用react-three-fiber的一些原因:

基于组件的场景:它允许您以声明的方式编写three.js对象,因此您可以通过创建可重用的React组件,利用道具、状态和钩子来构建场景。请记住,您基本上可以编写整个three.js对象目录及其所有属性。

内置助手:它提供了一些有用的功能,如raycaster,它允许您在每个网格上访问所有有用的指针事件,如onClick、onPointerOver、onPointerOut等。就像任何DOM元素一样。

钩子:它已经提供了很多钩子,比如useFrame,它允许我们将函数附加到raf循环中(甚至覆盖默认的循环),以及useThree,我们可以从中获得有用的对象,如渲染器、场景、相机等。

调整大小:如果您不需要特殊的自定义调整大小逻辑,它已经为您处理了相机的更改,并且画布会独立调整大小。您甚至可以访问视口的大小(基于三维坐标渲染的四边形的大小)。

无依赖:由于它是一个渲染器,因此不依赖于three.js版本,因此您可以自由选择自己喜欢的版本。

仅在需要时重新渲染:它与任何React组件一样工作,它会根据依赖关系的变化(状态、存储等)更新自己。

接下来,让我们使用react-three-fiber。

步骤1-设置项目

首先,您需要定义Canvas组件。它内部的所有内容都将添加到主场景中(由react three fiber定义)。

在代码编辑器中打开/src/App.js文件。用以下新行替换代码:

import { Canvas } from "react-three-fiber";

function App() {
  return (
    <div className="App">
      <Canvas>
        <!--
          <Children/> // any three.js object (mesh, group, etc.)
          <Children/> // any three.js object (mesh, group, etc.)
        -->
      </Canvas>
    </div>
  );
}

export default App;

第一步已经完成。通过这几行代码,您已经创建了画布、相机(透视相机,但您可以自定义它)和场景。

步骤2-添加带有网格的组

让我们比较一下如何创建基本网格并将其添加到组中的两种方法。

以下是定义网格并使用JavaScript将其添加到组的示例:

const group = new Group();
const geo = new BoxBufferGeometry(2,2,2);
const mat = new MeshStandardMaterial({color: 0x1fbeca});
const mesh = new Mesh(geo, mat);
group.position.set(0,0.1,0.1);
group.add(mesh);
scene.add(group);

这里是使用声明方法的相同网格:

<group position={[0,0.1,0.1]}>
  <mesh>
    <boxBufferGeometry attach="geometry" args={[0.047, 0.5, 0.29]} />
    <meshStandardMaterial attach="material" color={0xf95b3c} />
  </mesh>
</group>

在声明性方法中,不需要添加场景,因为它是自动添加的,因为它属于画布的子对象。

只需将所需参数作为args属性传递,然后可以将所有其他属性设置为props。

步骤3-创建多个立方体

现在假设您想要创建多个多维数据集并将它们添加到组中。

在普通的JavaScript中,您需要创建一个类(是否创建,取决于您喜欢的方法)来创建和处理它们,并将它们推送到数组中,等等。

使用R3F,您可以将组件数组添加到函数返回的内容中,作为任何DOM元素。您甚至可以传递一个道具并在内部应用它。

export default () => {
  const nodesCubes = map(new Array(30), (el, i) => {
    return <Cube key={i} prop1={..} prop2={...}/>;
  });

  return (
    <group>
      { nodesCubes }
    </group>
  );
};

步骤4-制作立方体动画

现在让我们制作它们的动画。react three-fiber为您提供了一种使用useFrame钩子将逻辑连接到raf循环中的好方法。

// ...
import {useFrame} from 'react-three-fiber'
// ...

const mesh = useRef()

useFrame( ({gl,scene,camera....}) => {
  mesh.current.rotation.y += 0.1
})

步骤5-处理有状态的属性

现在,让我们在悬停或单击时更改一些属性。您不需要设置任何光线投射器,因为它已经为您定义好了。由于您正在使用React,因此可以利用useState hook。

//...
const mesh = useRef()
const [isHovered, setIsHovered] = useState(false);
const color = isHovered ? 0xe5d54d : 0xf95b3c;

const onHover = useCallback((e, value) => {
    e.stopPropagation(); // stop it at the first intersection
    setIsHovered(value);
  }, [setIsHovered]);
//...

<mesh 
  ref={mesh}
  position={position}
  onPointerOver={e => onHover(e, true)}
  onPointerOut={e => onHover(e, false)}
>
  <boxBufferGeometry attach="geometry" args={[0.047, 0.5, 0.29]} />
  <meshStandardMaterial color={color} attach="material" />
</mesh>

现在,让我们根据用户的单击将立方体的状态从“active”修改为“inactive”,从而更改颜色和动画。和以前一样,您可以使用state并使用内置的函数onClick。

//...
const [isHovered, setIsHovered] = useState(false);
const [isActive, setIsActive] = useState(false);
const color = isHovered ? 0xe5d54d : (isActive ? 0xf7e7e5 : 0xf95b3c);

  const onHover = useCallback((e, value) => {
    e.stopPropagation();
    setIsHovered(value);
  }, [setIsHovered]);

  const onClick = useCallback(
    e => {
      e.stopPropagation();
      setIsActive(v => !v);
    },
    [setIsActive]
  );

  // raf loop
  useFrame(() => {
    mesh.current.rotation.y += 0.01 * timeMod;
    if (isActiveRef.current) { // a ref is needed because useFrame creates a "closure" on the state
      time.current += 0.03;
      mesh.current.position.y = position[1] + Math.sin(time.current) * 0.4;
    }
  });
//...

return (
  <mesh
    ref={mesh}
    position={position}
    onClick={e => onClick(e)}
    onPointerOver={e => onHover(e, true)}
    onPointerOut={e => onHover(e, false)}
  >
    <boxBufferGeometry attach="geometry" args={[0.047, 0.5, 0.29]} />
    <meshStandardMaterial color={color} attach="material" />
  </mesh>
);

步骤6-添加环境光和点光

和以前一样,如果要添加一些灯光,则需要创建一个函数(或组件)并将其添加到场景图中:

return (
  <>
    <ambientLight intensity={0.9} />
    <pointLight intensity={1.12} position={[0, 0, 0]} />
  </>
)

步骤7-添加旋转

最后,让我们将自定义逻辑添加到渲染循环中。

让我们在所有立方体的容器组上添加一个旋转。您可以简单地转到组组件,使用useFrame钩子并在那里设置旋转,就像以前一样。

useFrame(() => {
  group.current.rotation.y += 0.005;
});

总结

自从我发现react three fiber以来,我已经将其用于所有react three.js项目,从最简单的演示到最复杂的演示。我强烈建议您在线阅读所有文档并概述所有功能,因为这里我们只介绍了其中的一些功能。

由于R3F是由Paul Henschel为您带来的,因此它与react spring完全兼容。这意味着你可以使用react spring来制作所有three.js的动画,直接访问元素,说实话,这完全令人震惊。

顺便说一句,如果你习惯于使用three.js,你就必须稍微改变一下你的交互和创建方式。它有一个学习曲线,如果你想保持最佳性能,比如使用共享材料、克隆网格等,你需要一些时间来改变方法,但根据我的经验,这绝对值得投入时间。

 类似资料: