Three.js重复请求预加载图片的问题及解决方法咨询
我之前也碰到过一模一样的问题,咱们先把背后的原因理清楚:
跨域资源的缓存标识差异:你用的是跨域图片(域名和当前页面不一致),隐藏的
img标签加载时没设置crossOrigin属性,但Three.js的TextureLoader在加载跨域图片时,会自动给内部创建的Image对象加上crossOrigin: 'anonymous'(这是Three.js处理跨域纹理的默认逻辑)。浏览器会把带跨域标识和不带跨域标识的请求判定为不同资源——哪怕URL完全一样,也会重新发起请求,不会命中缓存。加载上下文的缓存隔离:普通
img标签是浏览器HTML解析阶段加载的,而TextureLoader是通过JS动态创建Image实例加载的。如果跨域配置不匹配,两者的缓存键就不一样,自然会出现重复请求。
而多个img标签引用同一张图时,所有标签都没设置crossOrigin,缓存键完全一致,所以只会请求一次。
下面给你几个实用的解决办法,按需选用:
方案一:给预加载img标签添加跨域属性
让预加载的img和Three.js的请求保持一致的跨域配置,这样浏览器就能识别为同一资源,直接命中缓存:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script> <!-- 添加crossOrigin属性,和Three.js默认配置对齐 --> <img class='preLoad' src='http://odf9m3avc.bkt.clouddn.com/1493817789932.jpg' style='display:none' crossOrigin='anonymous'>
let texture = new THREE.TextureLoader().load("http://odf9m3avc.bkt.clouddn.com/1493817789932.jpg")
方案二:直接复用预加载的img元素作为纹理源
既然图片已经通过img标签加载完成,直接把这个DOM元素传给Three.js创建纹理,彻底避免重复请求:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script> <img class='preLoad' src='http://odf9m3avc.bkt.clouddn.com/1493817789932.jpg' style='display:none' crossOrigin='anonymous'>
// 获取预加载完成的img元素 const preloadedImg = document.querySelector('.preLoad'); // 直接用该元素创建纹理,无需调用load方法 const texture = new THREE.Texture(preloadedImg); // 手动标记纹理需要更新,确保Three.js识别到图片已加载 texture.needsUpdate = true;
方案三:用Three.js自身加载器统一预加载
放弃HTML的img标签,直接用Three.js的TextureLoader配合LoadingManager管理预加载,所有纹理由Three.js统一处理,自然不会出现重复请求:
// 创建加载管理器,可监听整体加载进度 const loadingManager = new THREE.LoadingManager(); const textureLoader = new THREE.TextureLoader(loadingManager); // 预加载目标纹理 let preloadedTexture; textureLoader.load("http://odf9m3avc.bkt.clouddn.com/1493817789932.jpg", (texture) => { preloadedTexture = texture; // 加载完成后再初始化场景逻辑 initScene(); }); function initScene() { // 直接使用预加载好的纹理 const material = new THREE.MeshBasicMaterial({ map: preloadedTexture }); // ...后续场景初始化代码 }
你可以做个小测试:给多个img标签中的某一个加上crossOrigin='anonymous',会发现浏览器也会发起两次请求——这正好验证了跨域属性对缓存标识的影响。
内容的提问来源于stack exchange,提问作者Yuri Tu

