Three.js is the 3d animation library which is used to design games, movies, scenes, animations, graphics, vfx etc. If you want to learn more about three.js then follow our introductory guide –
In this article, we will learn about react-three-fiber which is a react renderer for three.js. According to official page –
React-three-fiber is required for react developers because you can build your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can tap into React’s ecosystem.
Introduction
React-three-fiber is the react renderer for three.js. It means that all the functionality of three.js is supported in this library. It doesn’t depend on any particular version of three.js. So, overall there are no limitations and no performance lag.
To install react-three-fiber, use npm –
npm install three @react-three/fiber
You should create your project using create-react-app.
Prerequisite
- You should have a basic understanding of three.js. You may learn the introduction and fundamentals from this guide.
- Be aware of React coding. This whole blog is filled with react articles, but you may refer official documentation.
Three.js Vs React-three-fiber
In this section, we will look at the differences between three.js and React-three-fiber. We need to understand how something in three.js could be done in react-three-fiber.
As we have discussed in our three.js guide, there are three main entities for creating an animation –
- Scene
- Camera
- Renderer
Let’s see three.js code first and then we will check out the equivalent react-three-fiber code –
<script> const scene = new THREE.Scene(); const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } ); const cube = new THREE.Mesh( geometry, material ); scene.add( cube ); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.z = 5; const renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); const animate = function () { requestAnimationFrame( animate ); cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render( scene, camera ); }; animate(); </script>
Here we have scene, box geometry, camera, renderer and animation loop.
In react-three-fiber, we have Canvas
, mesh
and useFrame()
hook. Let’s see their functions and how they are used –
<Canvas>
<Canvas>
is the component provided by react-three-fiber which handles the scene, camera and auto re-rendering. So, a single <Canvas>
code could handle this much of three.js code –
<script> const scene = new THREE.Scene(); // const geometry = new THREE.BoxGeometry(); // const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } ); // const cube = new THREE.Mesh( geometry, material ); // scene.add( cube ); const camera = new THREE.PerspectiveCamera( 75, parentDivWidth / parentDivHeight, 0.1, 1000 ); // camera.position.z = 5; const renderer = new THREE.WebGLRenderer(); renderer.setSize( parentDivWidth, parentDivHeight ); document.querySelector('#canvas-container').appendChild( renderer.domElement ); const animate = function () { requestAnimationFrame( animate ); // cube.rotation.x += 0.01; // cube.rotation.y += 0.01; renderer.render( scene, camera ); }; animate(); </script>
Here is the equivalent react-three-fiber code –
import { Canvas } from '@react-three/fiber' export default function App() { return ( <div id="canvas-container"> <Canvas /> </div> ) }
If you want to change the width or height of canvas, then change the size of parent container, i.e. #canvas-container
. The Canvas will automatically resize.
Since Canvas
component handles both scene and camera, so there are few props which could come handy in setting properties like camera fov, aspect ratio, far clip etc. This below table will help you in setting Canvas options –
PROP | DESCRIPTION | DEFAULT |
---|---|---|
children | Threejs jsx elements or regular components | |
gl | Props that go into the default renderer, or your own renderer | {} |
camera | Props that go into the default camera, or your own THREE.Camera | { fov: 75, near: 0.1, far: 1000, position: [0, 0, 5] } |
shadows | Props that go into gl.shadowMap, can also be set true for PCFsoft | false |
raycaster | Props that go into the default raycaster | {} |
vr | Switches renderer to VR mode, then uses gl.setAnimationLoop | false |
mode | React mode: legacy, blocking, concurrent | blocking |
resize | Resize config, see react-use-measure’s options | { scroll: true, debounce: { scroll: 50, resize: 0 } } |
orthographic | Creates an orthographic camera | false |
dpr | Pixel-ratio, use window.devicePixelRatio, or automatic: [min, max] | undefined |
linear | Switch off automatic sRGB encoding and gamma correction | false |
flat | Use THREE.NoToneMapping instead of THREE.ACESFilmicToneMapping | false |
onCreated | Callback after the canvas has rendered (but not yet committed) | (state) => {} |
onPointerMissed | Response for pointer clicks that have missed any target | (event) => {} |
<mesh>
This component holds the geometries and materials. Like in three.js, we create geometry, material and a mesh, here we have components for all of them. Suppose our three.js code is –
const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } ); const cube = new THREE.Mesh( geometry, material );
The equivalent code in react-three-fiber is –
<mesh> <boxGeometry /> <meshBasicMaterial color={"#ff0000"} /> </mesh>
-
THREE.Mesh
is represented by<mesh>
component. -
Three.BoxGeometry
is represented by<boxGeometry />
component. -
Three.MeshBasicMaterial
is represented by<meshBasicMaterial />
component.
To include the mesh in the scene, three.js code is –
scene.add( cube );
But in react library, we put the <mesh>
component as child of <Canvas>
component.
<Canvas> <mesh> <boxGeometry /> <meshBasicMaterial color={"#ff0000"} /> </mesh> </Canvas>
You can have any number of mesh objects within your canvas.
useFrame()
To run an animation loop, three.js uses requestAnimationFrame
function. The same could be achieved using useFrame()
hook in react-three-fiber. Note: Fiber hooks can only be called inside a <Canvas>
parent so you need to call it from a separate component.
import React from 'react'; import { Canvas, useFrame } from '@react-three/fiber' const AnimateFrame = (props) => { useFrame(({ clock }) => { props.meshRef.current.rotation.x += 0.01; }); return null; } export default function App() { const myMesh = React.useRef(); return ( <div id="canvas-container"> <Canvas> <mesh ref={myMesh}> <boxGeometry /> <meshBasicMaterial color={"#ff0000"} /> </mesh> <AnimateFrame meshRef={myMesh} /> </Canvas> </div> ) }
Here we have created a ref variable, myMesh
which is passed as ref prop in <mesh>
. To play animation using useFrame()
, we created a different component, AnimateFrame
. Then, we included this component as a child of <Canvas>
and passed myMesh
as prop because we want to rotate mesh object and for that we need our <mesh>
reference in AnimateFrame
.