一個有必要實現的需求 因為項目中需要使用canvasTexture(一個threejs3d引擎中的材質類型),繪製大量的圖片,每次使用都會請求大量的oss圖片資源,雖然重覆請求會有磁碟緩存但畢竟這個磁碟緩存時效過短, 這裡需要瞭解一下知識才能正常閱讀。 Transferable objects ht ...
一個有必要實現的需求
因為項目中需要使用canvasTexture(一個threejs3d引擎中的材質類型),繪製大量的圖片,每次使用都會請求大量的oss圖片資源,雖然重覆請求會有磁碟緩存但畢竟這個磁碟緩存時效過短,
這裡需要瞭解一下知識才能正常閱讀。
Transferable objects https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects
Web Worker https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API
OffScreenCanvas https://developer.mozilla.org/zh-CN/docs/Web/API/OffscreenCanvas
需要註意項目所處瀏覽器環境是否支持其中的某些Api
-
因為有了將有了將圖片緩存到本地的需求,那麼大量的資源緩存必然是使用indexedDB了
-
其次為了方便存儲和使用就需要將圖片專為Blob對象。我們如果在程式中批量的將 canvasTexture 輸出為圖片並專為Blob對象並存到本地的話,會因為大量長時間的占用主線程造成頁面渲染幀時隔過長,造成卡頓影響用戶體驗,
-
那麼我們就需要將canvasTexture輸出圖片和轉為Blob對象這個耗時的過程放到worker中進行
-
而如果要在worker中進行操作我們需要用到OffScreenCanvas來進行圖片的繪製輸出和轉為Blob對象
-
雖然worker可以傳遞OffScreenCanvas對象但是無法傳遞它的渲染空間Context所以我們只能在主線程中把canvasTexture中的畫面輸出為ArrayBuffer然後傳遞給worker中新創建的OffScreenCanvas然後通過OffScreenCanvas重新繪製並輸出為Blob對象返回給主線程進行存儲(ArrayBuffer,和 Blob都是可轉移對象Transferable object 所以我們不需要擔心它們的通信效率)自此這個流程就算完成了
這段代碼是對普通圖片進行緩存操作
//此段以及下一段代碼中都使用了localforage(一個封裝了web端多個本地存儲策略的npm包)這個Api作為存儲策略
setImageLocalCache(image, key) {
const cacheKey = key
const ofsCanvas = new OffscreenCanvas(image.width, image.height);
let context = ofsCanvas.getContext('2d')
context.drawImage(image, 0, 0, image.width, image.height)
const imageData = context.getImageData(0, 0, ofsCanvas.width, ofsCanvas.height);
const dataArray = imageData.data; //Unit8ClampedArray
const arrayBuffer = dataArray.buffer; // ArrayBuffer
const worker = new Worker('worker/makeBlobCache.js')
worker.postMessage({
arrayBuffer,
width: image.width,
height: image.height
}, [arrayBuffer])
context.clearRect(0, 0, ofsCanvas.width, ofsCanvas.height)
context = null
worker.onmessage = (e) => {
localforage.setItem(cacheKey, e.data).then(() => {
URL.revokeObjectURL(URL.createObjectURL(e.data)) // 存儲結束後釋放Blob對象
})
worker.terminate(); //釋放worker線程
}
}
這段代碼是使用緩存的資源操作
let blob = localforage.getItem(cacheKey)
if(blob) {
const image = new Image()
image.src = URL.createObjectURL(blob)
blob = null
image.onerror = (e) => {
console.log(e)
}
image.onload = () => {
console.log('執行到這裡圖片就載入完成了')
URL.revokeObjectURL(url)
}
}
這段代碼是上述兩段代碼中的worker文件代碼
self.onmessage = (e) => {
const arrayBuffer = e.data.arrayBuffer;
const width = e.data.width;
const height = e.data.height;
const uint8View = new Uint8ClampedArray(arrayBuffer);
const imageData = new ImageData(uint8View, width, height);
const offscreen = new OffscreenCanvas(width, height)
let ctx = offscreen.getContext('2d')
ctx.putImageData(imageData, 0, 0)
offscreen.convertToBlob({
type: 'image/png',
quality: 1
}).then(blob => {
ctx.clearRect(0, 0, offscreen.width, offscreen.height);
ctx = null;
self.postMessage(blob)
})
};