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挂钩 。
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可以创建什么的想法,可以在此处 , 此处和此处查看我们的最新项目。
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对象,因此我们可以利用props
, states
和hooks
来创建可重复使用的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
上访问所有有用的指针事件,如onClick
, onPointerOver
, onPointerOut
……就像任何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
从中可以获取有用的对象,例如renderer
, scene
, camera
,…
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组件使用,并在依赖项更改(状态,存储等)上进行更新。
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 (
);
}
const rootElement = document.getElementById("root");
ReactDOM.render( , 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!
第一步完成。 仅用这几行,我们就已经创建了canvas
, camera
(一个透视图,但是您可以自定义它)和scene
,我们不需要关心调整大小。 真棒 !
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
// 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,所以我将不再比较这两种方法。
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 ;
});
return (
{ nodesCubes }
);
};
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
})
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.
现在让我们假设您要在hover
或click
上更改一些属性。 您不需要设置任何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]);
//...
onHover(e, true)}
onPointerOut={e => onHover(e, false)}
>
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 (
onClick(e)}
onPointerOver={e => onHover(e, true)}
onPointerOut={e => onHover(e, false)}
>
);
That’s it. Easy-peasy.
而已。 十分简单。
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 (
<>
>
)
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?
仅此而已,看起来简单而直接吧?
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
素材设置动画,直接访问该元素,说实话,这完全是令人讨厌的事情。
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