在React Native中用Three.js加载obj&mtl文件时遇document未定义问题
解决React Native中MTLLoader加载时"Can't find variable: document"的问题
这个坑我之前在React Native结合WebGL加载3D模型时踩过,核心原因很直白:Three.js官方的MTLLoader依赖的TextureLoader是为浏览器环境设计的,它内部会调用浏览器的document对象创建图片元素,但React Native里根本没有这个浏览器API,直接用自然会报错。下面给你几个实用的解决办法:
方法1:自定义适配React Native的TextureLoader
我们可以自己实现一个不依赖document的纹理加载器,用React Native原生的Image模块来加载图片,替换掉MTLLoader默认的TextureLoader。
步骤:
- 导入React Native的
Image组件,创建自定义加载器类:
import { Image } from 'react-native'; import * as THREE from 'three'; class RNTextureLoader extends THREE.Loader { load(url, onLoad, onProgress, onError) { // 处理RN的资源路径:本地require的资源需要转成可访问的uri const resolvedUri = typeof url === 'number' ? Image.resolveAssetSource(url).uri : url; // 用RN的Image获取图片信息并加载 Image.getSize(resolvedUri, (width, height) => { const image = new Image(); image.src = resolvedUri; image.onload = () => { const texture = new THREE.Texture(image); texture.needsUpdate = true; // 标记纹理需要更新 onLoad(texture); }; image.onerror = (err) => { onError?.(err); }; }, (err) => { onError?.(err); }); } }
- 在使用MTLLoader时,替换默认的纹理加载器:
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'; import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'; const mtlLoader = new MTLLoader(); // 用自定义的RNTextureLoader替换默认加载器 mtlLoader.setTextureLoader(new RNTextureLoader()); // 加载MTL文件并后续加载OBJ模型 mtlLoader.load( './assets/model.mtl', (materials) => { materials.preload(); const objLoader = new OBJLoader(); objLoader.setMaterials(materials); objLoader.load('./assets/model.obj', (object) => { // 将模型添加到WebGL场景中 scene.add(object); }); }, (xhr) => console.log(`加载进度: ${(xhr.loaded / xhr.total) * 100}%`), (err) => console.error('MTL加载失败:', err) );
方法2:使用适配好的第三方库(更省心)
如果不想自己写加载器,可以直接用react-native-three这个库,它是专门为React Native封装的Three.js工具集,里面的所有加载器都已经适配了RN环境,不会出现浏览器API依赖的问题。
步骤:
- 安装依赖(注意和你的RN版本兼容):
npm install react-native-three # 或者用yarn yarn add react-native-three
- 直接导入使用封装好的加载器:
import { MTLLoader, OBJLoader } from 'react-native-three'; // 使用方式和官方Three.js几乎一致,无需额外适配 const mtlLoader = new MTLLoader(); mtlLoader.load('./assets/model.mtl', (materials) => { materials.preload(); const objLoader = new OBJLoader(); objLoader.setMaterials(materials); objLoader.load('./assets/model.obj', (object) => { scene.add(object); }); });
额外注意事项
- 确保模型和纹理文件放在RN项目的可访问路径下(比如
assets文件夹),并且在metro.config.js中配置了资源解析规则,避免加载失败。 - 本地资源尽量用
require('./assets/texture.png')的方式引入,或者确保路径是RN能识别的绝对URI。
内容的提问来源于stack exchange,提问作者Aditya
相关产品推荐
相关产品推荐

