DEV Community


Posted on

A Comprehensive Guide to 3D Animations in React Native with three.js

Have you ever seen those mindblowing 3D websites and wondered how they were built?

Most of them are built with the help of Three.js, which is a javascript library used to render 3d graphics using WebGL.

Three.js is also compatible with React Native, and in this tutorial, you learn the fundamentals of Three.js by building exciting 3d animations for the Nike app.

Let’s build our first 3d scene

We are going to use React Three Fiber, which is a React renderer for three.js. That means that we are going to write code the React way, which we are used to such as reusable components that react to state.

The difference in code can be seen in the below example (on the left we have three codes and on the right the equivalent in React Three Fiber).

threejs vs react-three-fiber

Setup an Expo project

Let’s get started by creating a blank expo project


npx create-expo-app FirstThreeApp
Enter fullscreen mode Exit fullscreen mode

Open the project in your editor of choice. In my case that’s VScode.

Also, let’s go ahead and run our project using npm start and then scan the QR with our phone using the Expo Go app.

Install dependencies

Now, let’s install three, @react-three/fiber and expo-gl. Make sure your terminal is in the root folder of your newly created project.

npx expo install three @react-three/fiber expo-gl expo-three
Enter fullscreen mode Exit fullscreen mode

Three.js fundamentals


The canvas is the component where our scene will be rendered in. Start by rendering Canvas as the root component of our app.

import { Canvas } from '@react-three/fiber';
export default function App() {  return <Canvas></Canvas>;}
Enter fullscreen mode Exit fullscreen mode

Render an object

To render something on the screen, we need to know its shape (or geometry), for example, the shape of a ball is a sphere.

Besides the shape, we also need to know what material it is made of: for example, a basketball ball is made of rubber and its color is orange.

The Geometry and the Material are the components of a Mesh.

Let’s render a basketball

<mesh>  <sphereGeometry />  <meshStandardMaterial color="orange" /></mesh> 
Enter fullscreen mode Exit fullscreen mode

Play with the position of the mesh on the screen, and with different geometries to see how it works.

Check out different geometries on threejs docs.

The problem is that we don’t really see the color orange that we wanted our ball to be.


Because we forget to turn on the lights.


Let’s add an ambientLight source that is similar to the daylight in a room. This type of light will light all the sides of the object in the same way and will not produce shadows

<ambientLight />
Enter fullscreen mode Exit fullscreen mode

An <pointLight> on the other hands is similar to a light bulb that will light more the part of the object that faces the light source. Because of that, a pointLight also needs a position in space.

<pointLight position={[10, 10, 10]} />
Enter fullscreen mode Exit fullscreen mode

Reusable components

The benefit of React Three Fiber is that we can build our scenes from reusable 3d react components.

Let’s create a reusable Box component

function Box(props) {  return (    <mesh {...props}>      <boxGeometry args={[1, 1, 1]} />      <meshStandardMaterial color={'orange'} />    </mesh>  );}
Enter fullscreen mode Exit fullscreen mode

Now, we can render multiple boxes by simply rendering the <Box /> component

<Box position={[0, -1.2, 0]} /><Box position={[0, 1.2, 0]} />
Enter fullscreen mode Exit fullscreen mode

We can then use state variables to manage the state of each Box

const [active, setActive] = useState(false);
<mesh  {...props}  onClick={(event) => setActive(!active)}  scale={active ? 1.5 : 1}>
Enter fullscreen mode Exit fullscreen mode

Animating the object

We can use the hook useFramewhich allows us to execute code on every rendered frame.

We will use it to rotate the boxes

const mesh = useRef();
useFrame((state, delta) => (mesh.current.rotation.y += delta));
<mesh  ref={mesh}   ...
Enter fullscreen mode Exit fullscreen mode

Rendering 3d models

Now that we covered the fundamentals, let’s start working on the real use case of displaying a 3d model of shoes.

Let’s first drag and drop the Airmax folder from the asset bundle into the assets directory of our project.

We have to configure the metro bundler to include these files as assets. For that update (or create) the file metro.config.js

// metro.config.jsmodule.exports = {  resolver: {    sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs'],    assetExts: ['glb', 'gltf', 'mtl', 'obj', 'png', 'jpg'],  },}
Enter fullscreen mode Exit fullscreen mode

Now we have to restart our server and clean the cache.

npm start -- --clear
Enter fullscreen mode Exit fullscreen mode

Create the Shoe component

function Shoe(props) {  return <mesh></mesh>;}
Enter fullscreen mode Exit fullscreen mode

Load the object

import { Canvas, useFrame, useLoader } from '@react-three/fiber';import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';...const obj = useLoader(OBJLoader,require('./assets/Airmax/shoe.obj'));
Enter fullscreen mode Exit fullscreen mode

And render the object using the primitive component


<primitive object={obj} scale={10} />
Enter fullscreen mode Exit fullscreen mode

At this point, we should get an error saying "A component suspended … ". To fix this, we need to wrap our <Shoe /> component inside a <Suspsense /> to render a fallback component while the shoe component is loading the necessary assets.

At this point, we should see the shape of the shoe. That’s perfect.

Let’s load the Materials

Let’s load the textures and the materials of our shoes.

import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
const material = useLoader(MTLLoader, require('./assets/Airmax/shoe.mtl'));
const obj = useLoader(  OBJLoader,  require('./assets/Airmax/shoe.obj'),  (loader) => {    material.preload();    loader.setMaterials(material);  });
Enter fullscreen mode Exit fullscreen mode


import { TextureLoader } from 'expo-three';
const [base, normal, rough] = useLoader(TextureLoader, [  require('./assets/Airmax/textures/BaseColor.jpg'),  require('./assets/Airmax/textures/Normal.jpg'),  require('./assets/Airmax/textures/Roughness.png'),]);
useLayoutEffect(() => {  obj.traverse((child) => {    if (child instanceof THREE.Mesh) { = base;      child.material.normalMap = normal;      child.material.roughnessMap = rough;    }  });}, [obj]);
Enter fullscreen mode Exit fullscreen mode
  • the map is our color map. This defines what color our object has

  • normalMap specifies the textures of our material. This changes the way each part of the shoe's color is lit.

  • roughnessMap specifies how rough the material appears from mirror-like material that reflects light to a diffuse material that does not reflect light.

Let’s animate the shoe

In this part of the tutorial, we will hook the 3d model up to the gyroscope sensor and will drive some animations based on this. This will give it a sense that by moving your phone in your hand, you can move the shoe on the screen.

Install Reanimated

Let’s install Reanimated library that will power our sensor-based animation.

npx expo install react-native-reanimated
Enter fullscreen mode Exit fullscreen mode

Add the babel plugin inside babel.config.js and then restart the server

plugins: ['react-native-reanimated/plugin'],
Enter fullscreen mode Exit fullscreen mode

Now, let’s use the Animated Sensor and send it’s date to our 3d Model Shoe

import { useAnimatedSensor, SensorType } from 'react-native-reanimated';
const animatedSensor = useAnimatedSensor(SensorType.GYROSCOPE, {  interval: 100,});
<Shoe animatedSensor={animatedSensor} />
Enter fullscreen mode Exit fullscreen mode

And in the useFrame of our Shoe, wqe can use the data from the sensor to animate to rotation of the shoe

useFrame((state, delta) => {  let { x, y, z } = props.animatedSensor.sensor.value;  x = ~~(x * 100) / 5000;  y = ~~(y * 100) / 5000;  mesh.current.rotation.x += x;  mesh.current.rotation.y += y;});
Enter fullscreen mode Exit fullscreen mode

Now, moving the phone will drive the animation of the shoe.


I hope you enjoyed this tutorial as much as I enjoyed putting it together.

Top comments (0)