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

如何让GLTFLoader的onProgress回调统计.gltf/.bin/.jpg所有文件的加载进度?

如何让GLTFLoader的onProgress回调统计.gltf/.bin/.jpg所有文件的加载进度?

你遇到的问题非常典型——GLTFLoader默认的onProgress回调仅会追踪主.gltf文件的加载进度,而它内部自动加载的.bin(模型二进制数据)和.jpg(纹理资源)这类依赖文件的进度不会被这个回调捕获,这就导致主文件加载完成后进度直接跳至100%,但实际子资源还在后台加载。

另外,先帮你纠正代码里的一个语法错误:gltfLoader.load的参数顺序是load(url, onLoad, onProgress, onError),但你把onProgress函数嵌套在了onLoad函数内部,这会导致这个进度回调根本不会被执行,这也是你看不到正确进度的原因之一。

接下来,我们用three.js内置的**THREE.LoadingManager**来解决核心问题——它是全局资源加载管理器,能追踪所有通过three.js加载器发起的资源请求(包括GLTFLoader内部加载的子资源),让我们可以准确统计所有文件的总加载进度。

方案1:按资源个数统计进度(简单易实现)

这种方式基于待加载的资源总数计算进度,每个资源加载完成后更新进度,适合对精度要求不高的场景:

// 1. 创建自定义LoadingManager实例
const loadingManager = new THREE.LoadingManager();

let totalResources = 0;
let loadedResources = 0;

// 所有资源开始加载时触发:记录总资源数
loadingManager.onStart = (url, itemsLoaded, itemsTotal) => {
  totalResources = itemsTotal;
  console.log(`开始加载 ${totalResources} 个资源`);
};

// 单个资源加载完成时触发:更新进度
loadingManager.onProgress = (url, itemsLoaded, itemsTotal) => {
  loadedResources = itemsLoaded;
  const progress = Math.round((loadedResources / totalResources) * 100);
  const loaderText = document.getElementById('loader-text');
  loaderText.textContent = `${progress}%`;
};

// 所有资源加载完成时触发:隐藏加载器
loadingManager.onLoad = () => {
  hideLoader();
  console.log('所有资源加载完成!');
};

// 资源加载出错时触发
loadingManager.onError = (url) => {
  console.error(`加载资源 ${url} 时发生错误`);
};

// 2. 将自定义LoadingManager传入GLTFLoader
const gltfLoader = new GLTFLoader(loadingManager);

// 显示加载器
document.getElementById('loader').style.display = 'block';

function hideLoader() {
  const loaderContainer = document.getElementById('loader');
  loaderContainer.style.display = 'none';
}

// 3. 加载GLTF模型
gltfLoader.load('./Model/Model.gltf', (gltf) => {
  scene.add(gltf.scene);
  // 无需在此调用hideLoader,LoadingManager的onLoad会统一处理
}, null, (error) => {
  console.error('加载模型时出错:', error);
});

方案2:按总字节数统计进度(更精准)

如果需要更贴合实际加载量的进度(按文件大小比例计算),我们可以监听每个资源的字节加载进度并累加统计:

let totalBytes = 0;
let loadedBytes = 0;
const resourceByteMap = new Map(); // 记录每个资源的总大小和已加载大小

// 覆盖默认XHR创建逻辑,监听每个资源的字节加载进度
const originalCreateXHR = THREE.DefaultLoadingManager.createXHR;
THREE.DefaultLoadingManager.createXHR = function() {
  const xhr = originalCreateXHR.apply(this, arguments);
  xhr.addEventListener('progress', (event) => {
    if (event.lengthComputable) {
      const resourceUrl = event.target.responseURL;
      // 记录当前资源的字节数据
      resourceByteMap.set(resourceUrl, {
        total: event.total,
        loaded: event.loaded
      });
      // 计算所有资源的总字节数和已加载字节数
      totalBytes = Array.from(resourceByteMap.values()).reduce((sum, item) => sum + item.total, 0);
      loadedBytes = Array.from(resourceByteMap.values()).reduce((sum, item) => sum + item.loaded, 0);
      // 更新进度文本
      if (totalBytes > 0) {
        const progress = Math.round((loadedBytes / totalBytes) * 100);
        const loaderText = document.getElementById('loader-text');
        loaderText.textContent = `${progress}%`;
      }
    }
  });
  return xhr;
};

const loadingManager = new THREE.LoadingManager();

// 所有资源加载完成时隐藏加载器
loadingManager.onLoad = () => {
  hideLoader();
  console.log('所有资源加载完成!');
};

// 资源加载错误处理
loadingManager.onError = (url) => {
  console.error(`加载资源 ${url} 时出错`);
};

// 初始化GLTFLoader并传入自定义管理器
const gltfLoader = new GLTFLoader(loadingManager);

// 显示加载器
document.getElementById('loader').style.display = 'block';

function hideLoader() {
  const loaderContainer = document.getElementById('loader');
  loaderContainer.style.display = 'none';
}

// 加载GLTF模型
gltfLoader.load('./Model/Model.gltf', (gltf) => {
  scene.add(gltf.scene);
}, null, (error) => {
  console.error('加载模型时出错:', error);
});

关键原理说明

LoadingManager是three.js的全局资源调度中心,GLTFLoader内部加载.bin和纹理资源时,会自动使用three.js的加载管理器发起请求。通过自定义LoadingManager,我们可以捕获到所有子资源的加载事件,从而实现全资源的进度统计。

备注:内容来源于stack exchange,提问作者Fedja Hadzibegovic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 14:23:00