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的一些原因:
基于组件的场景:它允许您以声明的方式编写three.js对象,因此您可以通过创建可重用的React组件,利用道具、状态和钩子来构建场景。请记住,您基本上可以编写整个three.js对象目录及其所有属性。
内置助手:它提供了一些有用的功能,如raycaster,它允许您在每个网格上访问所有有用的指针事件,如onClick、onPointerOver、onPointerOut等。就像任何DOM元素一样。
钩子:它已经提供了很多钩子,比如useFrame,它允许我们将函数附加到raf循环中(甚至覆盖默认的循环),以及useThree,我们可以从中获得有用的对象,如渲染器、场景、相机等。
调整大小:如果您不需要特殊的自定义调整大小逻辑,它已经为您处理了相机的更改,并且画布会独立调整大小。您甚至可以访问视口的大小(基于三维坐标渲染的四边形的大小)。
无依赖:由于它是一个渲染器,因此不依赖于three.js版本,因此您可以自由选择自己喜欢的版本。
仅在需要时重新渲染:它与任何React组件一样工作,它会根据依赖关系的变化(状态、存储等)更新自己。
接下来,让我们使用react-three-fiber。
首先,您需要定义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;
第一步已经完成。通过这几行代码,您已经创建了画布、相机(透视相机,但您可以自定义它)和场景。
让我们比较一下如何创建基本网格并将其添加到组中的两种方法。
以下是定义网格并使用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。
现在假设您想要创建多个多维数据集并将它们添加到组中。
在普通的JavaScript中,您需要创建一个类(是否创建,取决于您喜欢的方法)来创建和处理它们,并将它们推送到数组中,等等。
使用R3F,您可以将组件数组添加到函数返回的内容中,作为任何DOM元素。您甚至可以传递一个道具并在内部应用它。
export default () => {
const nodesCubes = map(new Array(30), (el, i) => {
return <Cube key={i} prop1={..} prop2={...}/>;
});
return (
<group>
{ nodesCubes }
</group>
);
};
现在让我们制作它们的动画。react three-fiber为您提供了一种使用useFrame钩子将逻辑连接到raf循环中的好方法。
// ...
import {useFrame} from 'react-three-fiber'
// ...
const mesh = useRef()
useFrame( ({gl,scene,camera....}) => {
mesh.current.rotation.y += 0.1
})
现在,让我们在悬停或单击时更改一些属性。您不需要设置任何光线投射器,因为它已经为您定义好了。由于您正在使用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>
);
和以前一样,如果要添加一些灯光,则需要创建一个函数(或组件)并将其添加到场景图中:
return (
<>
<ambientLight intensity={0.9} />
<pointLight intensity={1.12} position={[0, 0, 0]} />
</>
)
最后,让我们将自定义逻辑添加到渲染循环中。
让我们在所有立方体的容器组上添加一个旋转。您可以简单地转到组组件,使用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,你就必须稍微改变一下你的交互和创建方式。它有一个学习曲线,如果你想保持最佳性能,比如使用共享材料、克隆网格等,你需要一些时间来改变方法,但根据我的经验,这绝对值得投入时间。