【问题标题】:next.js and three.js model(gltf model) loading two timenext.js 和three.js 模型(gltf 模型)加载两次
【发布时间】:2022-09-30 22:14:47
【问题描述】:

我正在将 Chakra-UI 与 next-js 一起使用
我只想加载一次模型。
我多次浏览所有代码,但我没有在代码中找到错误。所以任何人都可以帮助我找出代码中的错误。以及为什么会这样。

index.js 文件:

import { Container } from \"@chakra-ui/react\";
import dynamic from \"next/dynamic\";
import PavanLogo from \"../components/PavanLogo\";
import PavanLogoLoader from \"../components/PavanLogoLoader\";

const LazyPavanLogo = dynamic(() => import(\"../components/PavanLogo\"), {
  ssr: false,
  loading: () => <PavanLogoLoader />,
});

export default function Home() {
  return (
    <>
      <LazyPavanLogo />
    </>
  );
}

PavanLogo.js 文件:

import { useState, useEffect, useRef, useCallback } from \"react\";
import * as THREE from \"three\";
import { OrbitControls } from \"three/examples/jsm/controls/OrbitControls\";
import { loadGLTFModel } from \"../lib/model\";
import { PavanSpinner, PavanContainer } from \"./PavanLogoLoader\";

function easeOutCirc(x) {
  return Math.sqrt(1 - Math.pow(x - 1, 4));
}

const PavanLogo = () => {
  const refContainer = useRef();
  const [loading, setLoading] = useState(true);
  const [renderer, setRenderer] = useState();
  const [_camera, setCamera] = useState();
  const [target] = useState(new THREE.Vector3(-0.5, 1.2, 0));
  const [initialCameraPosition] = useState(
    new THREE.Vector3(
      20 * Math.sin(0.2 * Math.PI),
      10,
      20 * Math.cos(0.2 * Math.PI)
    )
  );
  const [scene] = useState(new THREE.Scene());
  const [_controls, setControls] = useState();

  const handleWindowResize = useCallback(() => {
    const { current: container } = refContainer;
    if (container && renderer) {
      const scW = container.clientWidth;
      const scH = container.clientHeight;

      renderer.setSize(scW, scH);
    }
  }, [renderer]);

  /* eslint-disable react-hooks/exhaustive-deps */
  useEffect(() => {
    const { current: container } = refContainer;
    if (container && !renderer) {
      const scW = container.clientWidth;
      const scH = container.clientHeight;

      const renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true,
      });
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(scW, scH);
      renderer.outputEncoding = THREE.sRGBEncoding;
      container.appendChild(renderer.domElement);
      setRenderer(renderer);

      // 640 -> 240
      // 8   -> 6
      const scale = scH * 0.005 + 4.8;
      const camera = new THREE.OrthographicCamera(
        -scale,
        scale,
        scale,
        -scale,
        0.01,
        50000
      );
      camera.position.copy(initialCameraPosition);
      camera.lookAt(target);
      setCamera(camera);

      const ambientLight = new THREE.AmbientLight(0xcccccc, 1);
      scene.add(ambientLight);

      const controls = new OrbitControls(camera, renderer.domElement);
      controls.autoRotate = true;
      controls.target = target;
      setControls(controls);

      loadGLTFModel(scene, \"/Model/pavan.gltf\", {
        receiveShadow: false,
        castShadow: false,
      }).then(() => {
        animate();
        setLoading(false);
      });
      let req = null;
      let frame = 0;
      const animate = () => {
        req = requestAnimationFrame(animate);

        frame = frame <= 100 ? frame + 1 : frame;

        if (frame <= 100) {
          const p = initialCameraPosition;
          const rotSpeed = -easeOutCirc(frame / 120) * Math.PI * 20;

          camera.position.y = 10;
          camera.position.x =
            p.x * Math.cos(rotSpeed) + p.z * Math.sin(rotSpeed);
          camera.position.z =
            p.z * Math.cos(rotSpeed) - p.x * Math.sin(rotSpeed);
          camera.lookAt(target);
        } else {
          controls.update();
        }

        renderer.render(scene, camera);
      };

      return () => {
        console.log(\"unmount\");
        cancelAnimationFrame(req);
        renderer.dispose();
      };
    }
  }, []);

  useEffect(() => {
    window.addEventListener(\"resize\", handleWindowResize, false);
    return () => {
      window.removeEventListener(\"resize\", handleWindowResize, false);
    };
  }, [renderer, handleWindowResize]);

  return (
    <PavanContainer ref={refContainer}>
      {loading && <PavanSpinner />}
    </PavanContainer>
  );
};

export default PavanLogo;

PavanLogoLoader.js 文件:

import { forwardRef } from \"react\";
import { Box, Spinner } from \"@chakra-ui/react\";

export const PavanSpinner = () => (
  <Spinner
    size=\"xl\"
    position=\"absolute\"
    left=\"50%\"
    top=\"50%\"
    ml=\"calc(0px - var(--spinner-size) / 2)\"
    mt=\"calc(0px - var(--spinner-size))\"
  />
);

export const PavanContainer = forwardRef(({ children }, ref) => (
  <Box
    ref={ref}
    className=\"pavan\"
    m=\"auto\"
    mt={[\"-20px\", \"-60px\", \"-120px\"]}
    mb={[\"-40px\", \"-140px\", \"-200px\"]}
    w={[280, 480, 640]}
    h={[280, 480, 640]}
    position=\"relative\"
  >
    {children}
  </Box>
));

const Loader = () => {
  return (
    <PavanContainer>
      <PavanSpinner />
    </PavanContainer>
  );
};

export default Loader;

当它运行时加载两个模型

logo loaded twise

    标签: reactjs three.js next.js 3d-model


    【解决方案1】:

    React.StrictMode 使加载徽标的 useEffect 运行两次。解决方案是在 next.config.js 中禁用它。

    reactStrictMode: false
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-13
      • 2020-04-25
      • 1970-01-01
      • 2021-05-10
      • 1970-01-01
      • 2015-02-12
      • 2021-10-19
      • 2023-03-18
      相关资源
      最近更新 更多