3、加载管理器基础:Three.js LoadingManager介绍、事件监听(onStart, onProgress, onLoad, onError)
好,咱们今天聊聊加载管理器。说实话,很多新手做 Three.js 项目时,最容易忽略的就是加载状态的管理。你想想看,一个 3D 场景里动不动就是几 MB 甚至几十 MB 的模型、贴图,用户点开页面后干等着,连个进度条都没有——这体验能好吗?
我个人习惯是,只要项目里涉及外部资源加载,第一件事就是先把 LoadingManager 安排上。它就像个监工,帮你盯着每一个资源的加载状态。
3.1 什么是 LoadingManager?
LoadingManager 是 Three.js 提供的一个加载管理器。它本身不负责加载资源,而是用来统一管理多个加载器的行为。说白了,它就是给 Loader 们配了一个「总指挥」。
默认情况下,每个 Loader(比如 TextureLoader、GLTFLoader)都有自己的内部管理器。但如果你想让它们共享同一个进度回调、同一个错误处理逻辑,那就得自己创建一个 LoadingManager 实例。
核心作用:
- 统一监听所有加载器的开始、进度、完成、错误事件
- 支持设置加载基路径(base URL)
- 支持跨加载器共享资源缓存
3.2 创建 LoadingManager
创建方式非常简单,直接 new 一个就行:
const manager = new THREE.LoadingManager();
然后把这个 manager 传给各个 Loader:
const textureLoader = new THREE.TextureLoader(manager);
const gltfLoader = new THREE.GLTFLoader(manager);
const audioLoader = new THREE.AudioLoader(manager);
嗯,这里要注意:不是所有 Loader 都支持传入 manager。Three.js 官方提供的 Loader 基本都支持,但第三方 Loader 就不一定了。我曾经在项目里遇到过一个第三方模型格式的 Loader,死活不认 manager,最后只能自己手动封装了一层。
3.3 四个核心事件
LoadingManager 提供了四个关键事件回调。我把它们整理成了表格,方便你对照:
| 事件 | 回调参数 | 触发时机 |
|---|---|---|
| onStart | (url, itemsLoaded, itemsTotal) | 每个资源开始加载时触发 |
| onProgress | (url, itemsLoaded, itemsTotal) | 每个资源加载进度更新时触发 |
| onLoad | () | 所有资源加载完成时触发 |
| onError | (url) | 某个资源加载失败时触发 |
3.4 实战:写一个完整的加载管理器
光说不练假把式。咱们直接写一个完整的例子:
const manager = new THREE.LoadingManager();
// 开始加载
manager.onStart = function(url, itemsLoaded, itemsTotal) {
console.log(`开始加载: ${url}`);
console.log(`已加载 ${itemsLoaded}/${itemsTotal}`);
// 这里可以显示一个加载动画
};
// 加载进度
manager.onProgress = function(url, itemsLoaded, itemsTotal) {
const progress = (itemsLoaded / itemsTotal) * 100;
console.log(`加载进度: ${progress.toFixed(1)}%`);
// 更新进度条
document.getElementById('progress-bar').style.width = progress + '%';
};
// 全部完成
manager.onLoad = function() {
console.log('所有资源加载完成!');
// 隐藏加载界面,显示场景
document.getElementById('loading-screen').style.display = 'none';
};
// 加载出错
manager.onError = function(url) {
console.error(`加载失败: ${url}`);
// 显示错误提示
document.getElementById('error-message').textContent =
`资源加载失败: ${url}`;
};
// 使用这个 manager 创建加载器
const loader = new THREE.TextureLoader(manager);
loader.load('textures/wood.jpg');
loader.load('textures/metal.jpg');
loader.load('textures/stone.jpg');
小技巧:onProgress 里的 itemsLoaded 和 itemsTotal 是按文件个数计算的,不是按字节。如果你需要精确的字节级进度,得自己用 XMLHttpRequest 去封装。
3.5 避坑指南
我曾经在一个电商项目里踩过一个坑:同时加载了 20 多个纹理和 3 个 GLTF 模型,结果 onLoad 事件一直不触发。排查了半天才发现,有个纹理的 URL 写错了,但 onError 也没被调用——因为那个 Loader 压根没绑定 manager。
所以这里给你三个建议:
- 统一管理:项目中所有 Loader 尽量使用同一个 manager 实例
- 错误处理要冗余:除了 onError,最好再加个全局的 try-catch
- 测试边界情况:比如网络断开、资源 404、跨域问题,这些都要提前测
注意:LoadingManager 的 onLoad 事件只有在所有通过该 manager 创建的 Loader 都完成加载后才会触发。如果你在 onLoad 之后又调用了 loader.load(),那 onLoad 不会再次触发。需要重新创建 manager 或者手动重置状态。
3.6 进阶:设置基路径
LoadingManager 还有一个很实用的功能——设置基路径。当你的资源都放在同一个目录下时,这个功能能省不少事:
const manager = new THREE.LoadingManager();
manager.setURLModifier(function(url) {
return '/assets/' + url;
});
// 现在只需要传文件名
const loader = new THREE.TextureLoader(manager);
loader.load('wood.jpg'); // 实际加载 /assets/wood.jpg
loader.load('metal.jpg'); // 实际加载 /assets/metal.jpg
这个 setURLModifier 其实是个万能钩子。你可以在里面做 URL 重写、添加版本号、甚至做资源映射。我个人习惯用它来做 CDN 路径切换——开发环境用本地路径,生产环境自动替换成 CDN 地址。
3.7 小结
LoadingManager 是 Three.js 资源管理的基石。它不复杂,但很实用。记住四个核心事件:onStart、onProgress、onLoad、onError,再配合 setURLModifier,基本上能覆盖 90% 的加载管理需求。
下一章咱们会深入讲 TextureLoader 和纹理加载的细节。到时候你会发现,有了 LoadingManager 的基础,理解纹理加载的进度控制会轻松很多。
一句话总结:LoadingManager 就是加载器的「总指挥」,让多个加载器步调一致,让你能精确掌控加载流程的每一个环节。