programing

Threejs를 React에 연결하는 방법

bestprogram 2023. 3. 13. 21:09

Threejs를 React에 연결하는 방법

저는 React와 함께 일하며 캔버스를 사용합니다.캔버스를 WebGL(Threejs Library)로 변경하고 싶습니다.이 라이브러리를 React에 연결하는 방법

몇 가지 요소가 있습니다.

<div ref="threejs"></div>

Threejs 라이브러리 콜을 위한 필드를 만드는 방법은 무엇입니까?다음과 같은 확장자를 사용하고 싶지 않습니다.react-threejs.

이 설정 방법의 예를 다음에 나타냅니다(데모를 참조).

import React, { Component } from 'react'
import * as THREE from 'three'

class Scene extends Component {
  constructor(props) {
    super(props)

    this.start = this.start.bind(this)
    this.stop = this.stop.bind(this)
    this.animate = this.animate.bind(this)
  }

  componentDidMount() {
    const width = this.mount.clientWidth
    const height = this.mount.clientHeight

    const scene = new THREE.Scene()
    const camera = new THREE.PerspectiveCamera(
      75,
      width / height,
      0.1,
      1000
    )
    const renderer = new THREE.WebGLRenderer({ antialias: true })
    const geometry = new THREE.BoxGeometry(1, 1, 1)
    const material = new THREE.MeshBasicMaterial({ color: '#433F81' })
    const cube = new THREE.Mesh(geometry, material)

    camera.position.z = 4
    scene.add(cube)
    renderer.setClearColor('#000000')
    renderer.setSize(width, height)

    this.scene = scene
    this.camera = camera
    this.renderer = renderer
    this.material = material
    this.cube = cube

    this.mount.appendChild(this.renderer.domElement)
    this.start()
  }

  componentWillUnmount() {
    this.stop()
    this.mount.removeChild(this.renderer.domElement)
  }

  start() {
    if (!this.frameId) {
      this.frameId = requestAnimationFrame(this.animate)
    }
  }

  stop() {
    cancelAnimationFrame(this.frameId)
  }

  animate() {
    this.cube.rotation.x += 0.01
    this.cube.rotation.y += 0.01

    this.renderScene()
    this.frameId = window.requestAnimationFrame(this.animate)
  }

  renderScene() {
    this.renderer.render(this.scene, this.camera)
  }

  render() {
    return (
      <div
        style={{ width: '400px', height: '400px' }}
        ref={(mount) => { this.mount = mount }}
      />
    )
  }
}

export default Scene

전체 화면의 예에 관심이 있을 수도 있습니다(GitHub 참조).

다음은 클래스 대신 리액트 훅을 사용하는 예입니다.

리액트 쓰리 파이버를 사용하면

npm install three @react-three/fiber

사용.

import React from 'react';
import React3 from 'react-three-renderer';
import * as THREE from 'three';
import ReactDOM from 'react-dom';

class Simple extends React.Component {
  constructor(props, context) {
    super(props, context);

    // construct the position vector here, because if we use 'new' within render,
    // React will think that things have changed when they have not.
    this.cameraPosition = new THREE.Vector3(0, 0, 5);

    this.state = {
      cubeRotation: new THREE.Euler(),
    };

    this._onAnimate = () => {
      // we will get this callback every frame

      // pretend cubeRotation is immutable.
      // this helps with updates and pure rendering.
      // React will be sure that the rotation has now updated.
      this.setState({
        cubeRotation: new THREE.Euler(
          this.state.cubeRotation.x + 0.1,
          this.state.cubeRotation.y + 0.1,
          0
        ),
      });
    };
  }

  render() {
    const width = window.innerWidth; // canvas width
    const height = window.innerHeight; // canvas height
    return (<React3
      mainCamera="camera" // this points to the perspectiveCamera which has the name set to "camera" below
      width={width}
      height={height}

      onAnimate={this._onAnimate}
    >
      <scene>
        <perspectiveCamera
          name="camera"
          fov={75}
          aspect={width / height}
          near={0.1}
          far={1000}
          position={this.cameraPosition}
        />
        <mesh
          rotation={this.state.cubeRotation}
        >
          <boxGeometry
            width={1}
            height={1}
            depth={1}
          />
          <meshBasicMaterial
            color={0x00ff00}
          />
        </mesh>
      </scene>
    </React3>);
  }
}
ReactDOM.render(<Simple/>, document.body);

2022년 7월 갱신

  • 리액트 훅
  • 반응 기능 구성 요소
  • WebGLrenderer.setAnimation고리
  • 타이프 스크립트
  • 창 크기 조정
  • 마운트 해제 시 WebGL 컨텍스트 폐기
import * as T from 'three';
import { useRef, useEffect, useCallback } from 'react';

class GL {
  scene: T.Scene = new T.Scene();
  renderer?: T.WebGLRenderer;
  camera?: T.PerspectiveCamera;
  width: number = 1;
  height: number = 1;
  destroyed: boolean = false;
  prevTime: number = 0;
  delta: number = 0;

  constructor() {}

  get aspect() {
    return this.width / this.height;
  }

  init ({ canvas, width, height }: Pick<GL, 'canvas' | 'width' | 'height'>) {
    this.width = width;
    this.height = height;
    this.camera = new T.PerspectiveCamera(70, this.aspect, 0.01, 10);
    this.camera.position.z = 3;
    this.scene.add(this.camera);

    /* ... place your objects here ... */
    this.scene.add(new T.AxisHelper(3));

    this.renderer = new T.WebGLRenderer({canvas});
    this.resize(width, height);
    this.renderer.setAnimationLoop(this.onLoop);
  }

  resize(width: number, height: number) {
    if (!this.camera || !this.renderer) return;
    this.camera.aspect = this.aspect;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(width, height);
    this.renderer.setPixelRatio(/* ... set your pixel ratio ... */);
  }

  onLoop: XRFrameRequestCallback = (time) => {
    if (!this.camera || !this.renderer) return;
    if (this.prevTime !== 0) this.delta = time - this.prevTime;
    /* do something with your delta and time */
    this.renderer.render(this.scene, this.camera);
    this.prevTime = time;
  };

  destroy() {
    this.renderer?.dispose();
    this.camera = undefined;
    this.destroyed = true;
  }
}

let gl = new GL();

const App: React.FC = () => {
  const refCanvas = useRef<HTMLCanvasElement>(null);

  const onResize = useCallback(() => {
    gl.resize(
      refCanvas.current.clientWidth,
      refCanvas.current.clientHeight
    );
  }, []);

  useEffect(() => {
    if (!refCanvas.current) return;
    const state = refCanvas.current;
    if (gl.destroyed) gl = new GL();
    gl.init({
      canvas: refCanvas.current,
      width: refCanvas.current.clientWidth,
      height: refCanvas.current.clientHeight,
    });
    window.addEventListener('resize', onResize);
    return () => {
      window.removeEventListener('resize', onResize);
      gl.destroy();
    };
  }, []);
  return <canvas ref={refCanvas} className="gl"></canvas>;
}

3.js의 첫 번째 예를 React로 변환했습니다.이것이 'react'에서 React 가져오기, '.App.css' 가져오기, '3'에서 *로 가져오기,

class App extends React.Component{
  constructor(props){
    super(props)
    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera(75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
      );
    this.renderer = new THREE.WebGL1Renderer();
        this.renderer.setSize( window.innerWidth, window.innerHeight );
    this.geometry = new THREE.BoxGeometry();
    this.material = new THREE.MeshBasicMaterial( { color: 0x32a852});
        this.cube = new THREE.Mesh( this.geometry, this.material)
  }
  animate = () => {
    requestAnimationFrame(this.animate)
    this.cube.rotation.x += 0.01;
    this.cube.rotation.y += 0.01;
    this.renderer.render(this.scene, this.camera);
    
  }
  componentDidMount() {
        document.body.appendChild( this.renderer.domElement );
        this.scene.add( this.cube );
        this.camera.position.z = 5;
        this.animate()
    }
  render(){
    return(
      <body id='document-body-01'>
      </body>
    );
  }
}

export default App;

즉, 이 세 가지 컴포넌트를 컨스트럭터, 애니메이션 기능, 컴포넌트 디마운트로 나눌 수 있습니다.이 .varname은 어디에나 배치할 수 있습니다.나는 건설업자가 좋은 곳이라고 느꼈다.이 방법으로 다른 예도 설정할 수 있습니다.

3개의 파이버와 3개의 drei 라이브러리를 사용하는 것을 추천합니다.

사용해보십시오.<Canvas></Canvas>리액트의 캔버스에 대고 말할 수 있습니다.useRef훅을 클릭합니다.

https://docs.pmnd.rs/react-three-fiber/getting-started/introduction

시작하기에 아주 좋은 문서들이 있으니 꼭 확인하세요!

언급URL : https://stackoverflow.com/questions/41248287/how-to-connect-threejs-to-react