useGLTF加载GLTF模型序列时如何配合Suspense使用startTransition
问题说明
当前使用useGLTF加载模型序列时,切换模型会触发Suspense fallback导致Canvas空白,目标是通过startTransition实现新模型加载完成前持续展示旧模型,完成无空白的流畅切换。
现有写法不生效的根本原因:
Model组件定义在App组件内部,每次状态更新都会重新生成组件引用,导致旧组件被直接卸载- 直接在transition中更新url时,@react-three/fiber独立渲染循环抛出的加载Promise不会被React DOM层的startTransition捕获,依然会触发最近的Suspense fallback
实现方案
核心思路是先预加载模型资源,等资源完全就绪后再在transition中更新渲染的模型地址,全程不会触发Suspense fallback,旧模型会一直保留到新模型可以直接渲染。
修复后的完整代码:
import { Suspense, useState, useTransition } from "react"; import { Canvas } from "@react-three/fiber"; import { Html, OrbitControls, useGLTF } from "@react-three/drei"; // Model组件移到外层定义,避免重渲染时组件重建导致旧模型卸载 const Model = ({ url }) => { const gltf = useGLTF(url); return <primitive object={gltf.scene} scale={2} />; }; export default function App() { const [index, setIndex] = useState(0); // 初始化直接赋值首个模型地址,避免初始空值报错 const [currentUrl, setCurrentUrl] = useState( `https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Box/glTF/Box.gltf` ); const [isPending, startTransition] = useTransition(); const handleShowRandom = () => { const nextIndex = Math.floor(Math.random() * modelNames.length); const nextModelName = modelNames[nextIndex]; const nextUrl = `https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/${nextModelName}/glTF/${nextModelName}.gltf`; // 提前预加载目标模型,资源写入缓存后再更新状态 useGLTF.preload(nextUrl); startTransition(() => { setIndex(nextIndex); setCurrentUrl(nextUrl); }); }; return ( <div style={{ height: "800px", position: "relative" }}> <button onClick={handleShowRandom} disabled={isPending}> {isPending ? "模型加载中..." : "随机加载模型"} </button> <Canvas> {/* fallback仅在首次进入页面加载时触发,切换模型不会再出现 */} <Suspense fallback={ <Html> <h1>首次资源加载中</h1> </Html> } > <Model url={currentUrl} /> </Suspense> <ambientLight intensity={0.8} /> <OrbitControls /> </Canvas> </div> ); } const modelNames = [ "Box", "Duck", "BrainStem", "BarramundiFish", "AntiqueCamera", "CesiumMan", "IridescenceSuzanne", "DamagedHelmet", "FlightHelmet", "IridescenceLamp" ];
可选优化点
- 利用
isPending状态可以给旧模型加透明度降低、角落加小型加载提示等效果,不打断当前浏览体验 - 如果需要新旧模型交叉淡入的丝滑效果,可以维护新旧两个url的状态,新模型加载完成后通过材质透明度控制做过渡动画,动画结束后再移除旧模型
- 预加载过的模型会被useGLTF自动缓存,重复切换同个模型不会重复发起网络请求
内容的提问来源于stack exchange,提问作者Bill O
相关产品推荐
相关产品推荐

