2d graphics in React with three.js

Each of you may need to work with graphics when building a React application. Or you will need to render a large number of elements, and do it efficiently and achieve high performance when redrawing elements. It can be animation or some kind of interactive component. Naturally, the first thing that comes to mind is Canvas. But then the question arises: "What context to use?" We have a choice - 2d context or WebGl . What about 2d graphics? Not everything is so obvious here.





When working on high-performance tasks, we tried both solutions to determine in practice which of the two contexts would be more efficient. As expected, WebGl defeated the 2d context, so it seems like the choice is straightforward.





. , WebGl. , , 2d context. , , – . pixi.js three.js – , .





Pixi.js three.js

, : pixi.j 2d-, three.js – 3d. , 2d 3d? , 3d- . ,





, : “ ?”. Camera – , scene renderer. . , , . , – scene. , , – scene. camera , renderer – , 3d- 2d-.





, , , , . , , , – . , z, . , .





, three.js 2d-. ? three.js.





  • -, . , : pixi.js – , three.js – three.interaction.





, , . pixi.js , . ? . . three.js, , .





  • SVG. , SVG , , . three.js , pixi.js – .





  • , three.js . , , 3d-, pixi.js .





, – three.js.





Three.js React

– react- “” three.js. 





react – react-three-fiber. , , . , three.js react-three-fiber . 





, . drei storybook . , , , - . 





– react. view - ? .





, three.js . , ES6 – .





. , – .





three.js three.js. , .





class Three {
  constructor({
    canvasContainer,
    sceneSizes,
    rectSizes,
    color,
    colorChangeHandler,
  }) {
    //        this
    this.sceneSizes = sceneSizes;
    this.colorChangeHandler = colorChangeHandler;
 
    this.initRenderer(canvasContainer); //  
    this.initScene(); //  
    this.initCamera(); //  
    this.initInteraction(); //    
    this.renderRect(rectSizes, color); //    
    this.render(); //  
  }
 
  initRenderer(canvasContainer) {
    //   (    WebGL2)
    // antialias    
    this.renderer = new THREE.WebGLRenderer({antialias: true});
 
    //  
    this.renderer.setSize(this.sceneSizes.width, this.sceneSizes.height);
 
    //   -,    
    canvasContainer.appendChild(this.renderer.domElement);
  }
 
  initScene() {
    //   
    this.scene = new THREE.Scene();
 
    //   
    this.scene.background = new THREE.Color("white");
  }
 
  initCamera() {
    //    (   2d)
    this.camera = new THREE.OrthographicCamera(
      this.sceneSizes.width / -2, //   
      this.sceneSizes.width / 2, //   
      this.sceneSizes.height / 2, //   
      this.sceneSizes.height / -2, //   
      100, //  
      -100 //  
    );
 
    //    
    this.camera.position.set(
      this.sceneSizes.width / 2, //   x
      this.sceneSizes.height / -2, //   y
      1 //   z
    );
  }
 
  initInteraction() {
    //   (    )
    new Interaction(this.renderer, this.scene, this.camera);
  }
 
  render() {
    //    (    )
    this.renderer.render(this.scene, this.camera);
  }
 
  renderRect({width, height}, color) {
    //   -    "height"   "width"
    const geometry = new THREE.PlaneGeometry(width, height);
 
    //     "color"
    const material = new THREE.MeshBasicMaterial({color});
 
    //   - 
    this.rect = new THREE.Mesh(geometry, material);
 
    //   
    this.rect.position.x = this.sceneSizes.width / 2;
    this.rect.position.y = -this.sceneSizes.height / 2;
 
    //   "three.interaction"
    //       
    this.rect.on("click", () => {
      //   
      this.colorChangeHandler();
    });
 
    this.scene.add(this.rect);
  }
 
  //     
  rectColorChange(color) {
    //   
    this.rect.material.color.set(color);
 
    //   (    )
    this.render();
  }
}
      
      



ThreeContauner, React- Three.





import {useRef, useEffect, useState} from "react";
 
import Three from "./Three";
 
//    
const sceneSizes = {width: 800, height: 500};
const rectSizes = {width: 200, height: 200};
 
const ThreeContainer = () => {
  const threeRef = useRef(); //       canvas
  const three = useRef(); //   ,   ,    
  const [color, colorChange] = useState("blue"); //     
 
  // Handler   ,   
  const colorChangeHandler = () => {
    //             
    colorChange((prevColor) => (prevColor === "grey" ? "blue" : "grey"));
  };
 
  //    Three,     three.js
  useEffect(() => {
    //    "Three"   ,   
    if (!three.current) {
      //    "Three",       three.js
      three.current = new Three({
        color,
        rectSizes,
        sceneSizes,
        colorChangeHandler,
        canvasContainer: threeRef.current,
      });
    }
  }, [color]);
 
  //        Three
  useEffect(() => {
    if (three.current) {
      //  ,     
      three.current.rectColorChange(color);
    }
  }, [color]);
 
  //      canvas (  three.js)
  return <div className="container" ref={threeRef} />;
};
 
export default ThreeContainer;
      
      



.





, , , .





.





, three.js React- - , . , , / . , , virtual dom React-. , react-three-fiber drei – React-.





:





import {useState} from "react";
import {Canvas} from "@react-three/fiber";
import {Plane, OrthographicCamera} from "@react-three/drei";
 
//    
const sceneSizes = {width: 800, height: 500};
const rectSizes = {width: 200, height: 200};
 
const ThreeDrei = () => {
  const [color, colorChange] = useState("blue"); //     
 
  // Handler   , 
  const colorChangeHandler = () => {
    //             
    colorChange((prevColor) => (prevColor === "white" ? "blue" : "white"));
  };
 
  return (
    <div className="container">
      {/*   ,      */}
      <Canvas className="container" style={{...sceneSizes, background: "grey"}}>
        {/*       three.js,     makeDefault, 
           ,       */}
        <OrthographicCamera makeDefault position={[0, 0, 1]} />
        <Plane
          //     
          onClick={colorChangeHandler}
          //        ,     three.js
          args={[rectSizes.width, rectSizes.height]}
        >
          {/*       three.js, 
                 attach     */}
          <meshBasicMaterial attach="material" color={color} />
        </Plane>
      </Canvas>
    </div>
  );
};
 
export default ThreeDrei;
      
      



, , . , ,   . , , . 





three.js React-. , .





! , .








All Articles