Learn by doing: React-three-fiber ep. 1

Sometimes things come along in my life and just speak to me. When I learned about react-three-fiber, this was one of those things. After fiddling with the code examples and getting a little familiar with the api, I decided it has some serious potential to change the way we experience the web.

With that said, I know Three.js has been around for a while and hasn't necessarily caused a massive disruption to experiences on the web. But with the adoption of React over the last few years and how easily this library puts WebGL in the hands of react devs, it could mean some seriously amazing things.

For starters, I know very little about Three.js and and WebGL in general but I'm going to use react-three-fiber as an opportunity to learn. I've read that you need a little understanding of Three.js in order to really use this library, but... we shall see 😉

Setup

I've decided to use Typescript with React because It's what I'm used to writing for work. We'll see if this causes any unforeseen headaches 😆. Reading through the docs, I know the project will need these dependencies:

  • React
  • Three.js
  • React-three-fiber
  • Drei

I'll start with a codesandbox site to make it easy to share. I'll probably build off of my first creation as a way to create momentum.

Let's start small and try to create some text on the screen. I found this example to guide me:

It appears the outer most component is Canvas . The first prop it takes is orthographic. From readings in the past, I believe this is type of camera? Let's see what the Three.js docs say :

In this projection mode, an object's size in the rendered image stays constant regardless of its distance from the camera.

This can be useful for rendering 2D scenes and UI elements, amongst other things.

Ah, so this keeps objects in some kind of flat visual consistency. Sweet 💾

Now, I want some text to just say "Test" because I am boring like that. From the example above, it looks like I need a Text component provided by Drei. So far I have nothing showing up on the screen 😂

My first hunch is that I need to add some styling with css to open the visual area. I added the library styled-components because I'm pretty familiar with it in my work life and it's easy to work with. After making the body of the document black in the css file, I created a little container component like so:

const Container = styled.div`
  width: 100%;
  height: 400px;
  border: 2px solid white;
`;

The white border is just to see what I am working with. I wrap the existing code in the container:

export default function App() {
  return (
    <Container>
      <Canvas orthographic>
        <TextComponent />
        <MapControls />
      </Canvas>
    </Container>
  );
}

Bam! Actually... I don't see anything yet. I'm going to copy paste some stuff from my example I found. Such as the props for the Text component I created. I probably need a color and a font size. My TextComponent ends up looking like this:

const TextComponent = () => {
  return (
    <Text color="white" fontSize={24}>
      Test
    </Text>
  );
};

Et voilà! I see some text. Awesome. Now to take some queues from the example above, I'm going to add the MapControls component. According the Drei docs ... they don't say anything about it. But judging by the example they provide, it seems to make our object draggable along the x and y axis as well as zoomable. Like a map map! Er... google map?

I should also understand these position properties they are assigning since they seem pretty essential to three dimensional arrangement. In the example I'm using as a guide, both the canvas and text components have position properties. So let's look it up in the docs! According to Three.js docs :

A Vector3 representing the object's local position. Default is (0, 0, 0).

Uh... ok so what's a Vector3 ?

Class representing a 3D vector. A 3D vector is an ordered triplet of numbers (labeled x, y, and z), which can be used to represent a number of things, such as: - A point in 3D space. - A direction and length in 3D space. In three.js the length will always be the Euclidean distance (straight-line distance) from (0, 0, 0) to (x, y, z) and the direction is also measured from (0, 0, 0) towards (x, y, z). - Any arbitrary ordered triplet of numbers.

Excellent! So position in our context maps to coordinates x, y, z in a three dimensional space. Love it. So if it change the values of the position prop on my <Text> component, I should see it move around in space.

Mostly works as expected except the z value doesn't do anything until it just disappears 🤷🏻 Maybe if I add the camera prop to the canvas, the position value there will have an effect. Here's where I'm at:

const Container = styled.div`
  width: 400px;
  height: 400px;
  border: 2px solid white;
`;

const TextComponent = () => {
  const { viewport } = useThree();
  return (
    <Text
      anchorX="center"
      anchorY="middle"
      color="white"
      maxWidth={(viewport.width / 100) * 90}
      fontSize={24}
      lineHeight={0.5}
      position={[0, 0, 0]}
    >
      Test
    </Text>
  );
};

export default function App() {
  return (
    <Container>
      <Canvas
        orthographic
        pixelRatio={window.devicePixelRatio}
        colorManagement
        camera={{ position: [0, 0, 0], zoom: 5, up: [0, 0, 0] }}
      >
        <TextComponent />
        <MapControls />
      </Canvas>
    </Container>
  );
}

After copy-pasting the camera prop with all the same values, I get a slightly more zoomed in text. Which would be expected, I suppose, given that the zoom property is set to 5. I think this is enough for my first episode (edition?) of this blog. Here's what I ended up with:

This will become my template for future blog posts. I think for the next react-three-fiber blog, I'll try to use a 3d object of some sort. And maybe add a shadow?

And if you actually read this whole thing, thank you thank you thank you. I don't know how you found me, but I really appreciate you taking the time to consider my words. Much love and gratitude.