翼度科技»论坛 编程开发 JavaScript 查看内容

offscreenCanvas+worker+IndexedDB实现无感大量图片缓存

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
一个有必要实现的需求

因为项目中需要使用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 所以我们不需要担心它们的通信效率)自此这个流程就算完成了


这段代码是对普通图片进行缓存操作
  1.     //此段以及下一段代码中都使用了localforage(一个封装了web端多个本地存储策略的npm包)这个Api作为存储策略
  2.     setImageLocalCache(image, key) {
  3.         const cacheKey = key
  4.         const ofsCanvas = new OffscreenCanvas(image.width, image.height);
  5.         let context = ofsCanvas.getContext('2d')
  6.         context.drawImage(image, 0, 0, image.width, image.height)
  7.         const imageData = context.getImageData(0, 0, ofsCanvas.width, ofsCanvas.height);
  8.         const dataArray = imageData.data; //Unit8ClampedArray
  9.         const arrayBuffer = dataArray.buffer; // ArrayBuffer
  10.         const worker = new Worker('worker/makeBlobCache.js')
  11.         worker.postMessage({
  12.             arrayBuffer,
  13.             width: image.width,
  14.             height: image.height
  15.         }, [arrayBuffer])
  16.         context.clearRect(0, 0, ofsCanvas.width, ofsCanvas.height)
  17.         context = null
  18.         worker.onmessage = (e) => {
  19.             localforage.setItem(cacheKey, e.data).then(() => {
  20.                 URL.revokeObjectURL(URL.createObjectURL(e.data)) // 存储结束后释放Blob对象
  21.             })
  22.             worker.terminate(); //释放worker线程
  23.         }
  24.     }
复制代码
这段代码是使用缓存的资源操作
  1.     let blob = localforage.getItem(cacheKey)
  2.     if(blob) {
  3.       const image = new Image()
  4.       image.src = URL.createObjectURL(blob)
  5.       blob = null
  6.       image.onerror = (e) => {
  7.         console.log(e)
  8.       }
  9.       image.onload = () => {
  10.         console.log('执行到这里图片就加载完成了')
  11.         URL.revokeObjectURL(url)
  12.       }
  13.     }
复制代码
这段代码是上述两段代码中的worker文件代码
  1. self.onmessage = (e) => {
  2.     const arrayBuffer = e.data.arrayBuffer;
  3.     const width = e.data.width;
  4.     const height = e.data.height;
  5.     const uint8View = new Uint8ClampedArray(arrayBuffer);
  6.     const imageData = new ImageData(uint8View, width, height);
  7.     const offscreen = new OffscreenCanvas(width, height)
  8.     let ctx = offscreen.getContext('2d')
  9.     ctx.putImageData(imageData, 0, 0)
  10.     offscreen.convertToBlob({
  11.         type: 'image/png',
  12.         quality: 1
  13.     }).then(blob => {
  14.         ctx.clearRect(0, 0, offscreen.width, offscreen.height);
  15.         ctx = null;
  16.         self.postMessage(blob)
  17.     })
  18. };
复制代码
来源:https://www.cnblogs.com/zhaowendao233/p/17839059.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具