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

一个有必要实现的需求

因为项目中需要使用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)
    })

};
相关推荐
牧艺8 小时前
登录页还在用渐变?我一口气做了 5 个能摸的背景动效:吹蒲公英、滴墨染水、点熔岩闷裂
前端·canvas·交互设计
Highcharts.js12 小时前
Highcharts 渲染方式解析:SVG 默认渲染与 Boost 模块的 WebGL 加速,以及它与其他可视化库的不同
svg·webgl·可视化·canvas·highcharts·渲染方式
AndrewHZ4 天前
图像处理入门018 | 简易图像浏览器开发:用 OpenCV + Tkinter 打造你的第一个图像 GUI 工具
图像处理·人工智能·opencv·canvas·tkinter·事件绑定
小小龙学IT4 天前
第一节 QML 背景介绍
c++·qt·js
rockey6275 天前
C#脚本引擎之AScript与Jurassic、Jint对比
javascript·c#·.net·js·script·动态脚本
进哥AI研习社6 天前
WebGL 与视频背景——首屏视觉层的性能博弈
性能优化·webgl·gpu·canvas·shader·帧率·视频背景
MicrosoftReactor10 天前
技术速递|Canvas 如何让 Agentic Workflow 更可见、可控、更高效
ai·agent·canvas
breeze jiang14 天前
NestJS MVC 实战:用 hello 项目 7 个文件讲清分层、装饰器、依赖注入与错误处理
node.js·mvc·js
weixin_4316004415 天前
前端数据埋点(1):一次点击如何变成一条埋点
前端·js·数据埋点
又吹风_Bassy16 天前
介于 NanoVG 和 Skia 之间:C++17 可嵌入式 2D 渲染库 WhatsCanvas 全面介绍
canvas·skia·2d渲染库·nanovg·开源渲染库·whatscanvas·whatsui