reactjs react-three - 3D模型在Android手机设备上显示一个空白的白色方块

xfb7svmp  于 2023-06-22  发布在  React
关注(0)|答案(2)|浏览(111)

我正在使用Nextjs和3D库(如Three和React-Three)开发一个组合应用程序。
我使用Vercel部署应用程序。
我遇到了一个问题,即部署的应用程序中的3D模型在Android手机上显示为空白的白色正方形:

  • 小米红米note7/ MI浏览器
  • 三星s10 / chrome浏览器。

这个问题有解决办法吗?
展开部位:https://portfolio2-nu-ruddy.vercel.app/
Git Repo:https://github.com/ItayTur/Portfolio

三维模型组件:

import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { useLoader, Canvas } from "@react-three/fiber";
import { Suspense, useEffect, useState } from "react";
import { OrbitControls, Preload } from "@react-three/drei";
import Loader from "./Loader";

const Computers = ({ isMobile = false }) => {
  const gltf = useLoader(GLTFLoader, "/desktop_pc/scene.gltf");
  return (
    <mesh>
      <hemisphereLight intensity={0.15} groundColor="black" />
      <pointLight intensity={1} />
      <spotLight
        position={[-20, 50, 10]}
        angle={0.12}
        penumbra={1}
        intensity={1}
        castShadow
        shadow-mapSize={1024}
      />
      <primitive
        scale={isMobile ? 0.7 : 0.75}
        position={isMobile ? [0, -3, -2.2] : [0, -3.25, -1.5]}
        rotation={[-0.01, -0.2, -0.1]}
        object={gltf.scene}
      />
    </mesh>
  );
};

const ComputersCanvas = () => {
  const [isMobile, setIsMobile] = useState(false);

  useEffect(() => {
    const mediaQuery = window.matchMedia("(max-width: 500px)");

    setIsMobile(mediaQuery.matches);

    const onMediaQueryChange = (event: MediaQueryListEvent) => {
      setIsMobile(event.matches);
    };

    mediaQuery.addEventListener("change", onMediaQueryChange);

    return () => {
      mediaQuery.removeEventListener("change", onMediaQueryChange);
    };
  }, []);
  return (
    <Canvas
      frameloop="demand"
      shadows
      camera={{ position: [20, 3, 5], fov: 25 }}
      gl={{ preserveDrawingBuffer: true }}
    >
      <Suspense fallback={<Loader />}>
        <OrbitControls
          enableZoom={false}
          maxPolarAngle={Math.PI / 2}
          minPolarAngle={Math.PI / 2}
        />
        <Computers isMobile={isMobile} />
      </Suspense>
      <Preload all />
    </Canvas>
  );
};

export default ComputersCanvas;
5w9g7ksd

5w9g7ksd1#

这个问题已经在我的三星Galaxy A32上重现。

解决方案

问题出在Hero.tsx;将alpha: true添加到webGL配置中。

// Hero.tsx
<Canvas
  frameloop="demand"
  shadows
  camera={{ position: [20, 3, 5], fov: 25 }}
  // add alpha: true here.
  gl={{ preserveDrawingBuffer: true, alpha: true }}
>
{/* ... */}
</Canvas>

说明

原因目前还不是很清楚。我只能猜测,因为控制台消息显示r3f示例没有被正确销毁。消息是从chrome://inspect获取的。

WARNING: Too many active WebGL contexts. Oldest context will be lost., THREE.WebGLRenderer: Context Lost.
这些消息意味着每次挂载3D画布时,都会创建另一个不必要的webGL示例。多画布的正确实现应该只包含一个webGL示例,并在画布manually之间共享或使用门户。
我假设webGL缓冲区以某种方式被损坏,因此通过更改配置强制后台透明。
该错误在桌面浏览器中不可重现;这可能需要进一步深入调查。

0pizxfdo

0pizxfdo2#

我试着调试你的代码。有些东西阻止在Android的Chrome浏览器中渲染您的Canvas组件。我试图解决它,它似乎起作用了。你能尝试用下面的代码更新Computers.txs文件吗?
其余代码应该相同。

const ComputersCanvas = () => {
    const [isMobile, setIsMobile] = useState(false);

    useEffect(() => {
        const mediaQuery = window.matchMedia("(max-width: 500px)");

        setIsMobile(mediaQuery.matches);

        const onMediaQueryChange = (event: MediaQueryListEvent) => {
            setIsMobile(event.matches);
        };

        mediaQuery.addEventListener("change", onMediaQueryChange);

        return () => {
            mediaQuery.removeEventListener("change", onMediaQueryChange);
        };
    }, []);

    const [initialized, setInitialized] = useState(false)

    useEffect(() => {
        if (!initialized) {
            setInitialized(true);
        }
    }, [initialized]);

    if (!initialized) {
        return <div></div>
    }

    return (
        <Canvas
            frameloop="demand"
            shadows
            camera={{position: [20, 3, 5], fov: 25}}
            gl={{preserveDrawingBuffer: true, alpha: true}}
        >
            <Suspense fallback={<Loader/>}>
                <OrbitControls
                    enableZoom={false}
                    maxPolarAngle={Math.PI / 2}
                    minPolarAngle={Math.PI / 2}
                />
                <Computers isMobile={isMobile}/>
            </Suspense>
            <Preload all/>
        </Canvas>
    );
};

这是可行的,因为最初我们是在渲染div元素。
在第一次渲染之后,我们强制它使用Canvas元素重新渲染。
希望这能解决你的问题。
Thanks:)

更新:

很抱歉,我无法解决您的问题,因为它需要尝试不同的方法。但是,我可以给予你一些建议和想法,可以简化你的过程。主要问题在于android/移动的浏览器上的canvas行为。浏览器限制画布上下文的数量。我看到它一般是八,但它们可以是任何数字。
您可能已经注意到控制台消息THREE.WebGLRenderer: Context Lost.

  • 你可以使用一个画布上下文并重复使用同一个。这可能会很麻烦,特别是对于你的应用。
  • 您可以在应用中尝试解决方法,即应用监听滚动并仅为活动/可见的视图创建画布。在当前状态下,您的应用正在为页面上的所有视图/组件创建画布。
  • 当然,你可以尝试谷歌搜索和搜索,其他人是如何克服类似的情况。
  • 你可以增加这个问题的奖金,以吸引注意力,等待好的解决方案。
  • 等等的。

相关问题