1

I am trying to use three.js in a React application however even the basic sample from the three.js docs fails to load on the canvas.

I first made a vanilla.js sandbox implementation with the sample which works fine. Then I ported it over to create a react+three.js minimal sandbox implementation which fails to work.

Can anyone have a look at it and point me in the right direction ?

class Viewer extends Component {
    state = {};

    scene = null;

    camera = null;

    renderer = new WebGLRenderer();

    inst = 0;

    viewerRef = React.createRef();

    componentDidMount() {
        const { domElement } = this.renderer;
        this.scene = new Scene();
        this.scene.background = new Color("#ccc");
        this.camera = new Camera(
        75,
        domElement.innerWidth / domElement.innerHeight,
        0.1,
        1000
        );
        this.renderer.setSize(domElement.innerWidth, domElement.innerHeight);
        this.viewerRef.current.appendChild(this.renderer.domElement);
        const geometry = new BoxGeometry(1, 1, 1);
        const material = new MeshBasicMaterial({ color: 0x05ff00 });
        const cube = new Mesh(geometry, material);

        this.scene.add(cube);

        this.camera.position.z = 5;
        this.display();
    }

    display = () => {
        this.renderer.render(this.scene, this.camera);
        requestAnimationFrame(this.display);
    };

    render = () => <div className="viewer" ref={this.viewerRef} />;
}
2
  • HERE is a three.js / react thing I did a while ago, maybe taking a look at it, specifically the Scene component will help you. Commented Dec 31, 2018 at 1:22
  • thank you all for taking the time to answer. I had abandoned my efforts, and have to revisit if this solved my issues. will soon respond. Commented Apr 23, 2019 at 9:21

3 Answers 3

2

Here is CodeSandBox that works with a basic React wrapper code for Three.js. It also has THREE.OrbitControls integration and scale on resize code:

https://codesandbox.io/s/github/supromikali/react-three-demo

Live demo: https://31yp61zxq6.codesandbox.io/

Here is a full code snippet in the case above listed links don't work for you:

index.js code

import React, { Component } from "react";
import ReactDOM from "react-dom";
import THREE from "./three";


class App extends Component {
    componentDidMount() {


        // BASIC THREE.JS THINGS: SCENE, CAMERA, RENDERER
        // Three.js Creating a scene tutorial
        // https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(
            75,
            window.innerWidth / window.innerHeight,
            0.1,
            1000
        );
        camera.position.z = 5;

        var renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);



        // MOUNT INSIDE OF REACT
        this.mount.appendChild(renderer.domElement); // mount a scene inside of React using a ref



        // CAMERA CONTROLS
        // https://threejs.org/docs/index.html#examples/controls/OrbitControls
        this.controls = new THREE.OrbitControls(camera);



        // ADD CUBE AND LIGHTS
        // https://threejs.org/docs/index.html#api/en/geometries/BoxGeometry
        // https://threejs.org/docs/scenes/geometry-browser.html#BoxGeometry
        var geometry = new THREE.BoxGeometry(2, 2, 2);
        var material = new THREE.MeshPhongMaterial( {
            color: 0x156289,
            emissive: 0x072534,
            side: THREE.DoubleSide,
            flatShading: true
        } );
        var cube = new THREE.Mesh(geometry, material);
        scene.add(cube);

        var lights = [];
        lights[ 0 ] = new THREE.PointLight( 0xffffff, 1, 0 );
        lights[ 1 ] = new THREE.PointLight( 0xffffff, 1, 0 );
        lights[ 2 ] = new THREE.PointLight( 0xffffff, 1, 0 );

        lights[ 0 ].position.set( 0, 200, 0 );
        lights[ 1 ].position.set( 100, 200, 100 );
        lights[ 2 ].position.set( - 100, - 200, - 100 );

        scene.add( lights[ 0 ] );
        scene.add( lights[ 1 ] );
        scene.add( lights[ 2 ] );



        // SCALE ON RESIZE

        // Check "How can scene scale be preserved on resize?" section of Three.js FAQ
        // https://threejs.org/docs/index.html#manual/en/introduction/FAQ

        // code below is taken from Three.js fiddle
        // http://jsfiddle.net/Q4Jpu/

        // remember these initial values
        var tanFOV = Math.tan( ( ( Math.PI / 180 ) * camera.fov / 2 ) );
        var windowHeight = window.innerHeight;

        window.addEventListener( 'resize', onWindowResize, false );

        function onWindowResize( event ) {

            camera.aspect = window.innerWidth / window.innerHeight;

            // adjust the FOV
            camera.fov = ( 360 / Math.PI ) * Math.atan( tanFOV * ( window.innerHeight / windowHeight ) );

            camera.updateProjectionMatrix();
            camera.lookAt( scene.position );

            renderer.setSize( window.innerWidth, window.innerHeight );
            renderer.render( scene, camera );

        }



        // ANIMATE THE SCENE
        var animate = function() {
            requestAnimationFrame(animate);

            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;

            renderer.render(scene, camera);
        };

        animate();
    }
    render() {
        return <div ref={ref => (this.mount = ref)} />;
    }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

three.js code (THREE.OrbitControls import)

import * as THREE from 'three';


window.THREE = THREE; // THREE.OrbitControls expects THREE to be a global object
require('three/examples/js/controls/OrbitControls');


export default {...THREE, OrbitControls: window.THREE.OrbitControls};

Result should look like this:

enter image description here

Sign up to request clarification or add additional context in comments.

1 Comment

Great demo! I had to convert the line this.controls = new THREE.OrbitControls(camera); to this.controls = new THREE.OrbitControls(camera, renderer.domElement); in order to get it to work. I'm using version 0.127.0 .
2

I am not sure why nobody has mentioned react-three-fiber. It's a React renderer for ThreeJS.

Best part is, using rollupjs, we can even tree-shake a lot of unnecessary code, that we won't use in our React ThreeJS app. Also, using react-three-fiber in a desired way, we can achieve 60FPS for our ThreeJS animations 🥰

To learn basics, just take a look into react-three-fiber Examples

Comments

0

There's a few issues with your sandbox at the moment, which aren't necessarily related to React.

  1. innerWidth and innerHeight are not defined. clientWidth and clientHeight are probably the fields you're looking for to get the dimensions of the dom element.

  2. The dimensions of the canvas are being read before it has been added into the document, meaning the dimensions of the element will always be 0 0. Insert the element first before computing the camera aspect ratio and setting the render size.

  3. Camera isn't intended to be used directly. The docs specify it as an abstract base class for cameras -- use PerspectiveCamera instead.

Here's a sample of the code with the above fixes

const { domElement } = this.renderer;

// Fix 1
this.viewerRef.current.appendChild(domElement);

// Fix 2
const width = domElement.clientWidth;
const height = domElement.clientHeight;

this.scene = new Scene();
this.scene.background = new Color("#ccc");

// Fix 3
this.camera = new PerspectiveCamera(
    75,
    width  / height,
    0.1,
    1000
);
this.renderer.setSize(width, height);
const geometry = new BoxGeometry(1, 1, 1);
const material = new MeshBasicMaterial({ color: 0x05ff00 });
const cube = new Mesh(geometry, material);

this.scene.add(cube);
this.camera.position.z = 5;
this.display();

Hope that helps!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.