前言
使用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彪叔” 可以及時收到更多有價值的文章。