react-three - 在安卓手机设备上显示的3D模型是一个空白的白色正方形。

4

我正在使用Nextjs和3D库(如Three和React-Three)开发一个投资组合应用程序。

我使用Vercel部署应用程序。

我遇到了一个问题,即在Android手机上部署的应用程序中,3D模型显示为空白白色正方形:

  • Xiaomi red mi note 7 / MI浏览器

  • Samsung s10 / chrome浏览器。

这个问题有解决办法吗?

已部署网站:https://portfolio2-nu-ruddy.vercel.app/

Git Repo:https://github.com/ItayTur/Portfolio

3D模型组件:

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;


你试过远程调试吗?远程调试 - Harsh Shah
1
这个问题在我的iPhone 13上无法复现。必须明确指出是在哪个设备和浏览器上发生的。 - sungryeol
@sungryeol 这种情况在安卓手机上发生:例如三星S10。 - Itay Tur
@harshShah 我还没有尝试过远程调试。 - Itay Tur
@sungryeol 发生此问题的浏览器有:Chrome 和 Mi - Itay Tur
2个回答

0
问题已在我的三星Galaxy A32上复现。
解决方案
问题出在Hero.tsx文件中;在webGL配置中添加alpha: true
// 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获取的。

enter image description here

警告:活动的WebGL上下文过多。最旧的上下文将会丢失。THREE.WebGLRenderer: 上下文丢失。
这些信息意味着每次挂载一个3D画布时,都会创建另一个不必要的WebGL实例。正确的多画布实现应该只保留一个WebGL实例,并在画布之间共享它,可以通过手动方式手动或使用portal来实现。
我假设WebGL缓冲区可能已经损坏,所以通过更改配置强制将背景设置为透明。
这个错误在桌面浏览器中无法重现,可能需要进行深入调查。

我部署了更新,但不幸的是,白屏问题仍然存在。尽管没有任何警告信号,一个白色方块仍然出现在3D模型的位置上。此外,应用有时会完全崩溃。 - Itay Tur
我部署了更新,但不幸的是,白屏问题仍然存在。尽管没有任何警告信号,一个白色方块仍然出现在3D模型的位置。此外,应用有时会完全崩溃。 - undefined
@ItayTur你确定你只应用了上面的解决方案吗?这与应用崩溃没有任何关系。试着完全不运行移动端-桌面端的代码。这是唯一可能导致问题的原因。 - sungryeol
@ItayTur你确定你只应用了上述解决方案吗?这应该与应用程序崩溃无关。试着完全运行没有移动-桌面代码的版本。这是唯一可能导致问题的原因。 - undefined
我尝试将移动桌面代码注释掉,但问题仍然存在。 您可以在这里查看更改:https://github.com/ItayTur/Portfolio/commit/f73aa0014d42bd03229bac5582f1aca406ca90da。 - Itay Tur
我尝试将移动桌面代码注释掉,但问题仍然存在。 您可以在此处查看更改:https://github.com/ItayTur/Portfolio/commit/f73aa0014d42bd03229bac5582f1aca406ca90da。 - undefined

0
我尝试调试你的代码。有些东西阻止了在安卓的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元素重新渲染。
希望这能解决你的问题。
谢谢 :)
更新:
很抱歉,我无法解决你的问题,因为需要尝试不同的方法。但是我可以给出一些建议和想法,可能会简化你的过程。 主要问题在于安卓/移动浏览器上的canvas行为。 浏览器限制了canvas上下文的数量。通常情况下,我看到的限制是八个,但实际上可以是任意数量。
你可能已经注意到了控制台消息THREE.WebGLRenderer: Context Lost.

  • 你可以只使用一个canvas上下文并重复使用它。这可能会很麻烦,特别是对于你的应用程序来说。
  • 你可以在你的应用程序中尝试一种变通方法,让你的应用程序监听滚动事件,并仅为当前活动/可见的视图创建canvas。在当前状态下,你的应用程序为页面上的所有视图/组件都创建了canvas。
  • 当然,你可以尝试搜索和查找其他人如何克服类似情况的方法。
  • 你可以增加这个问题的奖励,以吸引注意力,并等待一个好的解决方案。
  • 等等。

最近的更新进行了一些改进。然而,我仍然遇到了一些问题,白色方块重新出现或应用在刷新页面时崩溃,并伴随着错误信息:"应用程序错误:发生了客户端异常"。 - Itay Tur
1
好的,谢谢。我会调试它,并很可能会找到解决方案后回来。另外,每次测试时,请在隐身模式下进行(针对这个安卓问题)。 - Mearaj
1
好的,谢谢。我会调试它,并很可能会回来带着解决方案。另外,每当你测试它时,请在无痕模式下测试(针对这个安卓问题)。 - undefined
1
非常感谢你所提供的所有帮助。非常感谢你。 - Itay Tur
1
非常感谢你所提供的所有帮助。非常感谢你。 - undefined
显示剩余13条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接