You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

useGLTF加载GLTF模型序列时如何配合Suspense使用startTransition

问题说明

当前使用useGLTF加载模型序列时,切换模型会触发Suspense fallback导致Canvas空白,目标是通过startTransition实现新模型加载完成前持续展示旧模型,完成无空白的流畅切换。
现有写法不生效的根本原因:

  1. Model组件定义在App组件内部,每次状态更新都会重新生成组件引用,导致旧组件被直接卸载
  2. 直接在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 21:57:36