当前位置: 首页 > article >正文

11.Three.js使用indexeddb前端缓存模型优化前端加载效率

11.Three.js使用indexeddb前端缓存模型优化前端加载效率

1.简述

在使用Three.js做数字孪生应用场景时,我们常常需要用到大量模型或数据。在访问我们的数字孪生应用时,每次刷新都需要从web端进行请求大量的模型数据或其他渲染数据等等,会极大的消耗时间,用户体验不好,因而我们可以通过indexeddb缓存我们的模型数据。

2.indexeddb简介

indexeddb 介绍和学习可以参考这里:https://zhuanlan.zhihu.com/p/429086021

IndexedDB主要用来客户端存储大量数据而生的,我们都知道cookie、localstorage等存储方式都有存储大小限制。如果数据量很大,且都需要客户端存储时,那么就可以使用IndexedDB数据库。它是一种前端的非关系型数据库,同样具有增删改查的功能。我下面封装了一个工具类dbUtils.js对模型数据进行缓存:

const DB_NAME = 'modeldb'; //数据库名称
const DB_VERSION = 1; //数据库版本号
const DB_STORE_NAME = 'model_glb_table'; //数据库仓库

function DBUtil() {
    this.db = null;
    // 根据模型的url地址,请求模型
    // 如果没有数据库中没有模型,则请求模型并放入缓存中,返回promise,带有模型的blob文件对象
    // 如果如果缓存中有模型了,就直接从缓存中取出
    this.get = async (url, onProgress) => {
        this.db = await this.initDataBase();
        let getRequest = this.db
            .transaction([DB_STORE_NAME], "readwrite") // 事务对象 指定表格名称和操作模式("只读"或"读写")
            .objectStore(DB_STORE_NAME) // 仓库对象
            .get(url);  // 通过主键(url)获取数据
        let that = this;
        return new Promise((resolve, reject) => {
            getRequest.onsuccess = function (event) {
                let modelFile = event.target.result;
                // 假如已经有缓存了 直接用缓存
                if (modelFile) {
                    if (onProgress) {
                        onProgress(100);
                    }
                    console.log("使用缓存");

                    resolve(modelFile.blob)
                } else {
                    // 假如没有缓存 请求新的模型存入
                    that.put(url, onProgress).then((blob) => {
                        resolve(blob)
                    }).catch(() => {
                        reject()
                    });
                }
            };
            getRequest.onerror = function (event) {
                // console.log('error', event)
                reject()
            }
        })
    }

    // 请求模型并放入缓存中
    this.put = async (url, onProgress) => {
        const response = await fetch(url);

        if (response.status !== 200) {
            throw new Error('Request failed');
        }

        const contentLength = response.headers.get('Content-Length');
        // console.log(contentLength)
        const totalBytes = parseInt(contentLength, 10);
        let downloadedBytes = 0;

        const readableStream = response.body;

        const { readable, writable } = new TransformStream();

        const writer = writable.getWriter();

        const reader = readableStream.getReader();

        const pump = async () => {
            const { done, value } = await reader.read();

            if (done) {
                writer.close();
                return;
            }

            writer.write(value);

            downloadedBytes += value.length;

            if (onProgress) {
                const progress = (downloadedBytes / totalBytes) * 100;
                console.log(progress.toFixed(2))
                onProgress(progress.toFixed(2));
            }

            return pump();
        };

        await pump();

        let blob = null;
        try {
            blob = await new Response(readable).arrayBuffer();
        } catch (e) {
            console.log('请求arrayBuffer失败,用blob方式')
            blob = await new Response(readable).blob();
        }

        let obj = {
            ssn: url
        }
        obj.blob = new Blob([blob])
        const inputRequest = this.db
            .transaction([DB_STORE_NAME], "readwrite")
            .objectStore(DB_STORE_NAME)
            .add(obj);

        return new Promise((resolve, reject) => {
            inputRequest.onsuccess = function () {
                console.log('glb数据添加成功');
                resolve(obj.blob);
            };
            inputRequest.onerror = function (evt) {
                console.log('glb数据添加失败', evt);
                reject();
            };
        });
    }

    // 打开数据库
    this.initDataBase = () => {
        if (!window.indexedDB) {
            console.log("浏览器不支持indexedDB缓存!!!")
            return;
        }
        let request = indexedDB.open(DB_NAME, DB_VERSION); //如果没有数据库,则创建,有数据库就链接
        return new Promise((resolve, reject) => {
            request.onerror = function () {
                // console.log("error: create db error");
                reject()
            };
            // 数据库创建或升级的时候会触发
            request.onupgradeneeded = function (evt) {
                evt.currentTarget.result.createObjectStore(
                    DB_STORE_NAME, { keyPath: 'ssn' });
            };
            // 数据库打开成功回调
            request.onsuccess = function (evt) {
                // console.log("onsuccess: create db success ");
                resolve(evt.target.result)
            };
        })
    }
}

3.Three.js加载模型

原本的three.js加载模型如下,这是最基础的加载模型的方法:

function initObject() {
        //再加载模型
        const objLoader = new THREE.GLTFLoader();
        objLoader.load(
          "./data/Soldier.glb",
          function (gltf) {
            let root = gltf.scene;
            root.position.set(0,0,0);
            root.scale.set(100,100,100);
            scene.add(root);

          },

          //加载回调
          function (xhr) {
            console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
          },
          //加载失败回调
          function (error) {
            console.log("An error happened");
          }
        );

    }

使用缓存后加载的方法:

function initObject() {
      new DBUtil().get("./data/Soldier.glb", (progress) => {
        console.log("progress", progress);

      }).then((blob) => {

        //再加载模型
        const objLoader = new THREE.GLTFLoader();
        let url = URL.createObjectURL(new Blob([blob]));
        objLoader.load(url, function (gltf) {
          let root = gltf.scene;
          root.position.set(0, 0, 0);
          root.scale.set(100, 100, 100);
          scene.add(root);

        },

          //加载回调
          function (xhr) {
            console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
          },
          //加载失败回调
          function (error) {
            console.log("An error happened");
          }
        );

      })


    }

4.验证检查

我们刷新网页后,查看应用程序中是否多了一个数据库,数据库中是否有数据

在这里插入图片描述

尝试删除该数据库后,再刷新页面。

对比数据库存在时,再次刷新页面。可以发现该数据库缓存存在时,模型渲染效率快了很多。特别时互联网访问时,还会有一个模型数据下载的过程,使用缓存可以直接省略下载的时间,效率上可以得到很大的提升。

视频地址:https://www.bilibili.com/video/BV1cQSzYLE9n/?vd_source=0f4eae2845bd3b24b877e4586ffda69a


http://www.kler.cn/a/377198.html

相关文章:

  • 掌握TensorFlow:Google人工智能学习框架的入门指南
  • GY-56 (VL53L0X) 激光测距
  • Google Guava 发布订阅模式/生产消费者模式 使用详情
  • Docker 镜像拉不动?自建 Docker Hub 加速站 解决镜像拉取失败
  • 线程池执行流程
  • 盘点10款录音转文字工具,帮你开启高效记录。
  • 「Mac畅玩鸿蒙与硬件23」鸿蒙UI组件篇13 - 自定义组件的创建与使用
  • vscode clangd for cuda 插件配置
  • 华为机试HJ19 简单错误记录
  • 管家婆财贸ERP BB087.销售单复制一行
  • 第二十五章 Vue父子通信之sync修饰符
  • JavaScript 生成二维码
  • 【棋盘覆盖——匈牙利算法】
  • Vue main.js引入全局progress组件原型实例,加载中动画组件原型实例
  • 在B端管理系统中,复杂或者DIY功能,都依赖哪些编辑器/设计器
  • 从技术与市场角度看:3D 创作软件与信创系统的 “距离”
  • node.js下载、安装、设置国内镜像源(永久)(Windows11)
  • Django-中间件
  • 如何理解ref,toRef,和toRefs
  • 《云计算网络技术与应用》实训8-1:OpenvSwitch简单配置练习
  • 写一个 EventBus 实现微信小程序的发布订阅,支持全局消息通知、跨页面通信,高效好用!
  • 形态学操作篇 原理公式代码齐活
  • Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
  • 《GBDT 算法的原理推导》 11-13初始化模型 公式解析
  • flask框架用法介绍(二):Flask和forms
  • 百度SEO与SEM到底有什么区别?福建企业老板们需要了解的关键点【百度SEO专家】