three react_使用react-three-fiber在React中编写Three.js

逑和蔼
2023-12-01

three react

So you want to write some 3D graphics/animations in your React apps using three.js? Let’s see how we can do just that, leveraging the react-three-fiber library.

那么您想使用three.js在React应用中编写一些3D图形/动画吗? 让我们看看如何利用react-three-fiber库做到这一点。

This article supposes you are familiar with React (with hooks) and three.js. If not, you can get a quick start on React hooks here and on three.js here.

本文假设您熟悉React (with hooks)three.js 。 如果没有,您可以在此处在three.js 快速了解React挂钩

入门与范例 (Getting started & Examples)

We’re going to use react-three-fiber, (for now-on called R3F), which is essentially a powerful React renderer for three.js, both for the web and with React Native.

我们将使用react-three-fiber (现在称为R3F ),它本质上是针对web和React Native的three.js的强大React渲染器。

We started using this library for this simple reason: if you’re usually working with React and you want to create a three.js experience, the standard approach can be painful to scale -> Some examples here, here and here.

我们开始使用该库的原因很简单:如果您通常使用React,并且想创建Three.js体验,那么标准方法可能很难扩展-> 这里这里这里的一些示例。

Working live examples: If you want to have an idea on what you can create using react-three-fiber you can check our last projects here, here and here.

工作示例 :如果您想了解使用react-three-fiber可以创建什么的想法,可以在此处此处此处查看我们的最新项目。

但是首先,为什么要React三根纤维? (But First, Why react-three-fiber?)

  • Component-based Scene: This is actually the main reason why I love this library. It allows us to write three.js objects in a declarative way, so we can build-up our scene creating re-usable React components, leveraging props, states and hooks. Keep in mind that you can write essentially three.js entire object catalogue and all its properties.

    基于组件的场景 :这实际上是我喜欢这个库的主要原因。 它允许我们以declarative方式编写three.js对象,因此我们可以利用propsstateshooks来创建可重复使用的React组件,从而构建场景。 请记住,您基本上可以编写three.js的整个对象目录及其所有属性。

  • Built-in helpers: It’s delivered with some useful functions like raycaster and it gives you access on each mesh to all the useful pointer-events like onClick, onPointerOver, onPointerOut,… Exactly like any DOM element.

    内置帮助器 :附带了一些有用的功能(如raycaster ,它使您可以在每个mesh上访问所有有用的指针事件,如onClickonPointerOveronPointerOut ……就像任何DOM元素一样 。

  • Hooks: It comes with a lot of hooks already, like useFrame that allows us to attach functions into the raf loop (or even override the default one), and useThree from where we can get useful objects like renderer, scene, camera,…

    挂钩 :它已经带有很多挂钩,例如useFrame ,它允许我们将功能附加到raf循环中(甚至覆盖默认值),以及useThree从中可以获取有用的对象,例如rendererscenecamera ,…

  • Resize: If you don’t need special custom resize logic, it already handles the camera changes for you, and the canvas resizes itself independently. Besides you can even get access to the size of the viewport (the size of the quad you are rendering based on 3D coordinates).

    调整大小 :如果您不需要特殊的自定义调整大小逻辑,则它已经为您处理了摄影机更改,并且画布会自行调整大小。 此外,你甚至可以访问该size的的viewport (你是基于三维坐标绘制四边形的大小)。

  • Dependency-free: Since it’s “just” a renderer it doesn’t rely on the three.js version, so you are free to choose your preferred version.

    无需依赖 :由于它只是一个渲染器,它不依赖Three.js版本,因此您可以自由选择首选版本。

  • Re-render only when needed: It works as any React component, it updates itself on a dependency change (state, store, etc).

    仅在需要时重新渲染 :它可以作为任何React组件使用,并在依赖项更改(状态,存储等)上进行更新。

让我们写一些three.js (Let’s Write Some three.js)

So, it’s time to give you a quick and simple example. First of all we need to define our Canvas component. Everything inside of it will be added to the main scene (defined by react-three-fiber for us).

因此,是时候给您一个简单的例子了。 首先,我们需要定义Canvas组件。 它里面的所有内容都会添加到主scene (由react-three-fiber为我们定义)。

In this example I’ll emphasize the code splitting in multiple files to show how cool is to work in components. It’s not performance-optimized, because it’s not the goal of this introduction. You can find the complete example with the final code here.

在此示例中,我将强调将代码拆分为多个文件,以显示在组件中工作的酷劲。 它不是性能优化的,因为这不是本介绍的目标。 您可以在此处找到带有最终代码的完整示例

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

function App() {
  return (
    <Canvas> // here you can pass a lot of options as prop
      <Children/> // any Threejs object (mesh, group or whatever)
      <Children/> // any Threejs object (mesh, group or whatever)
    </Canvas>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

The first step is done. With just these few lines we’ve already created the canvas, the camera (a perspective one but you can customize it) and the scene and we don’t need to care about the resize. Awesome!

第一步完成。 仅用这几行,我们就已经创建了canvascamera (一个透视图,但是您可以自定义它)和scene ,我们不需要关心调整大小。 真棒 !

仅添加一个网格组 (Add a group with just a mesh)

Let’s compare the two approaches on how to create a basic mesh and add it into a group:

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

// plain JS
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);

// declarative
<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>
// we don't need to add to the scene because since it's a child of the canvas it's automatically added

As you can see it’s pretty straight-forward. You just need to pass the required arguments as args property and then you can set all the other properties as props.

如您所见,这非常简单。 您只需要将必需的参数作为args属性传递,然后可以将所有其他属性设置为props。

Since it is supposed that you’re already familiar with three.js, I won’t compare the two approaches anymore .

因为假设您已经熟悉three.js,所以我将不再比较这两种方法。

创建多个多维数据集 (Create multiple cubes)

Suppose now that you want to create multiple cubes and add them into the group. In plain vanilla JavaScript you need to create a class (or not, depends on your preferred approach) to create and handle them, and push them into an array and so on. With R3F you can just add an array of components into what the function returns, as any DOM-element. You can even pass a prop and apply it internally.

现在假设您要创建多个多维数据集并将其添加到组中。 在普通JavaScript中,您需要创建一个class (或不取决于您的首选方法)来创建和处理它们,并将它们推入数组等。 使用R3F您可以像在任何DOM元素中一样,将一系列组件添加到函数返回的内容中。 您甚至可以传递道具并在内部应用它。

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

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

动画他们 (Animate them)

Let’s now animate them. react-three-fiber provides you a great way to attach your logic into the raf loop, using the useFrame hook.

现在让它们动画。 react-three-fiber提供了一个很好的方法,可以使用useFrame挂钩将逻辑附加到raf循环中。

// ...
import {useFrame} from 'react-three-fiber'
// ...
const mesh = useRef()

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

使用状态处理属性 (Handle properties with states)

Let’s now suppose you want to change some properties on hover or on click. You don’t need to set any raycaster because it’s already defined for you. Since we are using React we can leverage the useState hook.

现在让我们假设您要在hoverclick上更改一些属性。 您不需要设置任何raycaster因为它已经为您定义了。 由于我们正在使用React,因此我们可以利用useState挂钩。

//...
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>

Let’s now change the color and the animation too, modifying the state of the cube from “active” to “inactive” based on the user’s click. As before we can play with state and use the built-in function onClick.

现在,我们也更改颜色和动画,根据用户的点击将多维数据集的状态从“活动”更改为“非活动”。 和以前一样,我们可以使用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>
);

That’s it. Easy-peasy.

而已。 十分简单。

添加一些灯光 (Add some lights)

As before if we want to add some lights we just need to create a function (or component) and add it into the scene graph.

和以前一样,如果我们想添加一些灯光,我们只需要创建一个函数(或组件)并将其添加到scene图中即可。

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

最后一步 (Last step)

We’ve already created a basic scene in three.js, but let’s do our last step just to show you how easy it is to add custom logic into the render-loop.

我们已经在three.js中创建了一个基本场景,但是让我们做最后一步只是向您展示将自定义逻辑添加到渲染循环中有多么容易。

Let’s add a rotation on the container group of all the cubes. You can just simply go to the group component, use the useFrame hook and set the rotation there, just like before.

让我们在所有多维数据集的容器组上添加一个旋转。 您可以像以前一样简单地转到group组件,使用useFrame钩子并在那里设置旋转角度。

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

That’s all, looks easy and straight-forward right?

仅此而已,看起来简单而直接吧?



奖金 (Bonus)

Since R3F is brought to you by Paul Henschel it’s totally compatible with react-spring. This means you can use react-spring to animate all your three.js stuff, accessing directly to the element, and to be honest it’s totally mind blowing 勞.

由于Paul Henschel为您带来了R3F ,因此它与react-spring完全兼容。 这意味着您可以使用react-spring为所有three.js素材设置动画,直接访问该元素,说实话,这完全是令人讨厌的事情。

最后一点要澄清 (Last Points to Clarify)

Since I’ve discovered react-three-fiber, I’ve used it for all my React three.js projects, from the simplest demo to the most complex one. I strongly suggest you to read all the documentation online and overview all the features because here we just covered some of them.

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

By the way, if you are used to working with three.js, you’ll have to switch a little how you interact / create stuff. It has, as everything, a learning curve and you’ll need some time to change the approach if you want to keep performance at its best, like for example using a shared material, cloning a mesh, etc, but based on my experience it’s absolutely worth the time invested.

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

翻译自: https://www.digitalocean.com/community/tutorials/react-react-with-threejs

three react

 类似资料: