介绍
Three.js 提供的 Loader 系列模块主要有:
Loaders:
- AnimationLoader
- AudioLoader
- BufferGeometryLoader
- Cache
- CompressedTextureLoader
- CubeTextureLoader
- DataTextureLoader
- FileLoader
- ImageBitmapLoader
- ImageLoader
- Loader
- LoaderUtils
- MaterialLoader
- ObjectLoader
- TextureLoader
LoaderingManager:
- LoadingManager
- DefaultLoadingManager:是 Three.js 中内置的 LoadingManager 单例。 ```typescript // 以下为截选代码 // 完整代码见:https://github.com/mrdoob/three.js/blob/380f0f63e27753f7a34ef2be836db09f1f9963f4/src/loaders/LoadingManager.js#L140-L142
const DefaultLoadingManager = new LoadingManager();
export { DefaultLoadingManager, LoadingManager };
<a name="MnZrg"></a># Loader在 Three.js 中,Loader 主要负责对远程静态资源的加载。并且,根据应用场景分为以下两种:1. 加载远程数据。比如 ImageLoader、FileLoader;1. 加载远程数据,并将其转换成 Three.js 的概念对象。比如 TextureLoader、MaterialLoader、BufferGeometryLoader、ObjectLoader、GLTFLoader。<a name="glgCW"></a>## load 方法在 Loader 基类中,只定义 load 方法的抽象接口,其具体的实现由具类负责。```typescript// 以下为截选代码// 完整代码见:https://github.com/mrdoob/three.js/blob/2a2a8a55a91778f4ae76f2d8ffa5f7f9f2f0a694/src/loaders/Loader.jsclass Loader {load( url, onLoad, onProgress, onError ) {}}
其中,onProgress 参数早已废弃了,但是为了保障接口的向后兼容,一直未从参数列表中移除。
关于 load 方法的具体实现,主要是负责向 url 请求数据,然后唤起相应的生命周期钩子。比如,FileLoader 的load 方法 :
// 以下为截选代码
// 完整代码见:https://github.com/mrdoob/three.js/blob/483cf30801069d24122a130dfcb9d85fd55474c2/src/loaders/FileLoader.js
class FileLoader extends Loader {
load( url, onLoad, onProgress, onError ) {
// create request
const req = new Request( url, {
headers: new Headers( this.requestHeader ),
credentials: this.withCredentials ? 'include' : 'same-origin',
// An abort controller could be added within a future PR
} );
// start the fetch
fetch( req )
.then( response => {
if ( response.status === 200 || response.status === 0 ) {
if ( typeof ReadableStream === 'undefined' || response.body.getReader === undefined ) {
return response;
}
// periodically read data into the new stream tracking while download progress
const stream = new ReadableStream();
return new Response( stream );
}
} )
.then( data => {
onLoad( data )
} )
.catch( err => {
onError( err )
} )
}
}
loadAsync 方法
是对 load 方法的 Promise 版。返回 Promise,其加载到的数据将被 resolve 返回。
// 以下为截选代码
// 完整代码见:https://github.com/mrdoob/three.js/blob/2a2a8a55a91778f4ae76f2d8ffa5f7f9f2f0a694/src/loaders/Loader.js
class Loader {
loadAsync( url, onProgress ) {
const scope = this;
return new Promise( function ( resolve, reject ) {
scope.load( url, resolve, onProgress, reject );
} );
}
}
HTTP 相关参数
对于 HTTP 请求,Loader 从接口上,目前只支持了 URL、Request Headers、Credentials 三个参数的设置,缺乏对其它参数的支持。(关于原生 fetch 方法的完整参数,可参见:MDN: fetch parameters。)
这样会面临一些明显的限制。比如,难以使用 Post 方法进行传参(因为其依托于 HTTP.body 字段)。而且即使是 Get 方法也需要在 Loader 类的外部自行将查询参数构造进 URL,使用起来不太方便。
所以从这一点看,Loader 只适合用于 GET 请求的应用场景。而且最好是「查询参数」相对固定的,比如静态资源。
CrossOrigin
目前只对 ImageLoader 有效。其中的原理是:
- 创建
HTMLElement image;
- 设置 image.crossOrigin 为 ImageLoader 的 crossOrigin 属性;
- 浏览器会根据 image.crossOrigin 的值,在发起对图片的 HTTP 请求时带上相应的 request headers;
- 从而,实现 CORS。 ```typescript // 以下为截选代码 // 完整代码见:https://github.com/mrdoob/three.js/blob/63632f2ae94a4b7b196bc6c7796e01085230ed1f/src/loaders/ImageLoader.js
class ImageLoader extends Loader {
load( url, onLoad, onProgress, onError ) {
const image = createElementNS( 'img' );
image.crossOrigin = this.crossOrigin;
}
}
<a name="UsbH1"></a>
# LoadingManager
<a name="NVFyt"></a>
## 主要功能
从目前看,LoadingManager 主要负责对其管理的多个 loaders 实例,进行一些统一处理:
- 提供了一些生命周期钩子 ,包括 onStart、onProgress、onLoad、onError。可用于实现在某个生命周期进行的统一处理逻辑。
- 提供了一个前置的 URL 拦截器。可用于添加对 URL 的统一处理逻辑。
```typescript
// 以下为截选代码
// 完整代码见:https://github.com/mrdoob/three.js/blob/2a2a8a55a91778f4ae76f2d8ffa5f7f9f2f0a694/src/loaders/LoadingManager.js
class LoadingManager {
constructor( onLoad, onProgress, onError ) {
const scope = this;
let urlModifier = undefined;
this.itemStart = function ( url ) {
scope.onStart( url, itemsLoaded, itemsTotal );
};
this.itemEnd = function ( url ) {
scope.onProgress( url, itemsLoaded, itemsTotal );
};
this.itemError = function ( url ) {
scope.onError( url );
};
this.resolveURL = function ( url ) {
return urlModifier( url );
};
this.setURLModifier = function ( transform ) {
urlModifier = transform;
};
}
}
Loader 对其的操作
Loader 对 LoadingManager 的操作主要分布在两处:
- 一处是 Loader 基类中;
- 一处是 Loader 具类的 load 方法中;
其中,Loader 基类对 LoadingManager 的操作,只有保存了 LoadingManager 的 ref 。
// 以下为截选代码
// 完整代码见:https://github.com/mrdoob/three.js/blob/380f0f63e27753f7a34ef2be836db09f1f9963f4/src/loaders/Loader.js#L3-L15
class Loader {
constructor( manager ) {
this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
}
}
而 Loader 具类的 load 方法,为主要地调用 LoadingManager 的地方。以 FileLoader 为例:
// 以下为截选代码
// 完整代码见:https://github.com/mrdoob/three.js/blob/380f0f63e27753f7a34ef2be836db09f1f9963f4/src/loaders/FileLoader.js#L6-L232
class FileLoader extends Loader {
load( url, onLoad, onProgress, onError ) {
url = this.manager.resolveURL( url );
if ( cached !== undefined ) {
this.manager.itemStart( url );
setTimeout( () => {
if ( onLoad ) onLoad( cached );
this.manager.itemEnd( url );
}, 0 );
return cached;
}
// start the fetch
fetch( req )
.catch( err => {
this.manager.itemError( url );
} )
.finally( () => {
this.manager.itemEnd( url );
} );
this.manager.itemStart( url );
}
}
适用场景
适合以下应用场景:
- 获取(GET)并加载远程数据资源。
- 远程数据的格式和 HTTP 请求配置基本固定,仅 URL 可变。这种情况下,远程数据服务,往往满足于某种标准协议,比如 Image、glTF、 3D Tiles、BufferGeoemtryJSON 等,只是不同的数据实体,被存放在在不同的 URL 上。
不适合于以下应用场景:
- 不适合对内存中的已存在数据进行二次处理和转换,因为其入参只是一个 string 类型的 URL。
具体案例分析:
- 用 Loader 加载后端后端数据,和我们自行定义服务数据请求类(比如,在前端项目中,毕竟常见的 DataSource / Service 类)加载,有什么区别?考虑的因素:
- 是否存在绝对性的技术限制。
- 用我们自定义的数据服务请求类,在 Three.js 中,有没有绝对性的技术限制?
- Three.js 内有没有哪些些接口,被限制为了 Loader 类?
- 如果直接看源码没有发现,没有。
- 可以看看 example,example也没有发现
- 可以想想 texture 有一个延迟加载显示的功能是怎么实现的,是否依赖了 Loader 类的接口定义?
- 是由 Texture 类 联合 needsUpdate 机制实现的,与 Loader 类 无关。
- 所以,结论没有
- 所以,使用我们自定义的数据服务请求类,应该没有绝对性的技术限制。
- Three.js 内有没有哪些些接口,被限制为了 Loader 类?
- 用 Loader 类加载后端服务数据,有没有绝对性的技术限制?
- 关于 HTTP 请求的接口,Loader 类接口只支持了 URL、Request Headers、Credentials 三个参数的设置,缺乏对其它参数的支持。关于原生 fetch 方法的完整参数,可参见:MDN: fetch parameters。
- 这会面临一些明显的限制。比如,无法实现 Post 方法的传参(因为其依托于 HTTP.body 字段)。而且即使是 Get 方法也需要在 Loader 类的外部自行将查询参数构造进 URL,使用起来不太方便。
- 所以目前看,从技术上只适合用于 GET 请求。而且最好是查询参数固定的,也就是静态资源。
- 关于 HTTP 请求的接口,Loader 类接口只支持了 URL、Request Headers、Credentials 三个参数的设置,缺乏对其它参数的支持。关于原生 fetch 方法的完整参数,可参见:MDN: fetch parameters。
- 用我们自定义的数据服务请求类,在 Three.js 中,有没有绝对性的技术限制?
- 是否符合社区使用习惯。
- 是否存在绝对性的技术限制。
