Three.js react js/next js 重复内容

Three.js react js/next js duplicated content

我在 react(next js) 中使用 Three.js,我创建的 Mesh 被复制了多次

import * as THREE from 'three';


function Index() {
    if (process.browser) {
        const scene = new THREE.Scene();
        const camera = new THREE.
            PerspectiveCamera(
                75, 
                window.innerWidth / window.innerHeight,
                0.1, 
                1000
            );

        const renderer = new THREE.WebGLRenderer();

        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        document.body.appendChild(renderer.domElement);

        console.log(scene.children);
        const geometry = new THREE.BoxGeometry();
        const material = new THREE.MeshBasicMaterial({color: 0x00ff00});
        const cube = new THREE.Mesh(geometry, material);
        scene.add(cube);
        console.log(scene.children);
        camera.position.z = 12;

        renderer.render(scene, camera);
        return <div> </div>
    }
    return null;

}

export default Index;

像这样:

然后控制台选项卡有:

目标是在屏幕上有 1 个立方体而不滚动(水平和垂直)。

我怀疑是 document.body.appendChild,我也没有找到替代方法。

你会想要在 React 中保留有效的东西 useEffect. This will run on the browser when the component mounts and re-run if its dependency array changes. A dependency would be an immutable value that represents state derived from hooks like useState

您也永远不想直接触摸 DOM,因为 React 不会知道您在那里所做的更改,从而导致重复和其他副作用。相反,您可以通过 useRef 挂钩访问带有 refs 的 DOM。这使我们可以使用 canvasRef.current.

获得 canvas 的可变访问器
import * as THREE from 'three';
import { useRef, useEffect } from 'react';

function Index() {
  const canvasRef = useRef();

  useEffect(() => {
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    );

    const renderer = new THREE.WebGLRenderer({ canvas: canvasRef.current });

    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(window.devicePixelRatio);

    console.log(scene.children);
    const geometry = new THREE.BoxGeometry();
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);
    console.log(scene.children);
    camera.position.z = 12;

    renderer.render(scene, camera);

    // Cleanup on unmount, otherwise stuff will linger in GPU
    return () => {
      renderer.forceContextLoss();
      renderer.dispose();
      cube.geometry.dispose();
      cube.material.dispose();
    };
  }, []);

  return <canvas ref={canvasRef} />;
}

export default Index;

请注意 returns 回调的效果。这是一个清理函数,在组件卸载时执行。我们这样做是因为 three.js 将几何体和 material 着色器程序上传到 GPU,这些程序不会被垃圾收集并且会持续存在。由于您必须自己管理生命周期,因此我强烈建议为 three.js 使用渲染器,例如 @react-three/fiber,这样您就可以更好地使用 React 生态系统。

import { Canvas } from '@react-three-fiber';

function Index() {
  return (
    <Canvas frameloop="demand" camera={{ position: [0, 0, 12] }}>
      <mesh>
        <boxGeometry />
        <meshBasicMaterial color={0x00ff00} />
      </mesh>
    </Canvas>
  );
}

export default Index;

这是因为脚本会在组件呈现时运行。所以最好的方法是制作一个 canvas 元素并使用 useEffect 钩子或 componentDidMount 方法,如果它是 class.

但就个人而言,我建议使用 react-three/fiberreact-three-next