乐趣区

关于前端:使用IndexedDB缓存给WebGL三维程序加速

前言

应用 webgl 开发三维利用的时候,常常会发现三维场景加载比较慢,往往须要期待挺长时间,这样用户的体验就很不敌对。造成加载慢的起因,次要是三维利用波及到的资源文件会特地多,这些资源文件次要是模型及其图片,往往这些模型和图片都会比拟大。

为了放慢三维场景的加快速度,能够应用 IndexedDB 在客户端进行资源缓存。IndexedDB,即客户端长久化数据库!应用本缓存技术,在首次拜访后,3D 场景中的文件级别数据将写入拜访设施本地缓存数据库,在客户端实现永恒的生命周期,革除浏览器缓存也不影响已缓存的 3D 模型文件。

IndexedDB 介绍

IndexedDB 是一个前端数据长久化解决方案(即前端缓存),由浏览器实现。
IndexedDB 又如下特点

  • 基于文件存储。意味着其容量可达到硬盘可用空间下限
  • 非关系型数据库。意味着扩大或膨胀字段个别毋庸批改数据库和表构造(除非新增字段用做索引)
  • 键值对存储。意味着存取毋庸字符串转换过程
  • 存储类型丰盛。意味着浏览器缓存中不再是只能存字符串了
  • 异步:意味着所有操作都要在回调中进行

本地浏览器领有三种永恒存储数据技术,别离为 Web Storage、IndexedDB、Web SQL。IndexedDB 具备查问高效、存储空间大和异步操作等技术特色,有微小的劣势。

存储空间大 。IndexedDB 的贮存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有下限。在 HTML5 本地存储中,IndexedDB 存储的数据则是最多的。

查问高效 。IndexedDB 是一种轻量级 NOSQL 数据库,是由浏览器自带。相比 Web Sql 更加高效,包含索引、事务处理和查问性能。

异步操作 。IndexedDB 操作时不会锁死浏览器,用户仍然能够进行其余操作,这与 LocalStorage 造成比照,后者的操作是同步的。异步设计是为了避免大量数据的读写,拖慢网页的体现。

与此同时,IndexedDB 外部采纳对象仓库存放数据。所有类型的数据都能够间接存入,包含 JavaScript 对象,满足了三维场景的存储须要。

因而 应用 IndexedDB 缓存是一种最为优异的前端缓存计划。像 Babylon.js, 其引擎层面曾经反对了 IndexedDB 缓存。能够参考如下文档:
https://doc.babylonjs.com/div…。

three.js 应用 IndexedDB 的思路

无关具体如何应用 IndexedDB,有很多材料进行介绍,此文不在赘述。

应用 IndexedDB 缓存模型资源,首先须要获取模型相干的资源,这些模型资源包含模型文件以及相干的图片文件。比方对于 GLTF 模型而言,其资源包含.gltf 的模型主文件,.bin 格局的文件,纹理贴图文件等等。首次加载加一个模型的时候,必定是加载网络上的资源文件,通过 threejs 的 LoadingMananger 能够收集一个 gltf 模型的各种资源文件。代码如下:

    const resourceCollector = [];
    const loadingManager = new LoadingManager();
    loadingManager.setURLModifier((url,path) => {console.log(url);
      if(url.startsWith("data:") || url.startsWith("blob:")) {return url;}
      resourceCollector.push(url);
      return url;
    });

上述代码 resourceCollector 收集了加载模型过程中所有的模型资源的地址。收集之后把所有资源存储到 IndexedDB 中:

saveGltfModel:async function(options,resourceCollector){
    const gltfUrl = options.gltfPath;
    const blobs = {};
    for(let i = 0;i < resourceCollector.length;i ++) {let url = resourceCollector[i];
      let blob = await loadAsBlob(url);
      blobs[url] = blob;
      await addToDatabase("model",{key:url,blob})
    }
    await addToDatabase("model_info",{key:gltfUrl,content:resourceCollector});
  },

其中 loadAsBlob 是把一个资源加载成为 blob 对象,代码如下:

  const xhr = new XMLHttpRequest();
  xhr.open("GET", url);
  xhr.responseType = "blob";
  xhr.onerror = function() {reject("Network error.")};
  xhr.onload = function() {if (xhr.status === 200) {resolve(xhr.response)}
      else {reject("Loading error:" + xhr.statusText)}
  };
  xhr.send();

而 addToDatabase 办法把资源增加到 IndexedDB 数据库。

function addToDatabase(storename, data) {const promise = new Promise( (resolve,reject) => {let store = database.transaction(storename, 'readwrite').objectStore(storename);
    let countReq = store.count(data.key);
    countReq.onsuccess = function(event) {console.log("count:",event.target.result);
      let count = event.target.result;
      if(count ==  0) {let request = store.add(data);
        request.onerror = function (event) {console.error('add 增加数据库中已有该数据')
          reject(event);
        };
        request.onsuccess = function (event) {console.log('add 增加数据已存入数据库')
          resolve(event);
        };
      }
    };
  });
}

下一次获取模型的时候,能够先判断是否以及本地存储,如果曾经本地存储,就能够间接从本地获取模型资源:

 if(this.indexDbCache && indexedDB) {if(database == null) {database = await initialDB();
      }
      const storeObject = await findInDatabase("model_info",key);
      if(storeObject) {return this.loadGltfInDb(options);
      }
    }

缓存成果测评

通过测试能够发现对于比拟大的场景,模型加载的速度能够进步几倍,十几倍甚至几十倍。由此可见,IndexedDB 缓存成果很显著。

如果对可视化感兴趣,能够和我交换,微信 541002349。

关注公号“ITMan 彪叔”能够及时收到更多有价值的文章。

退出移动版