three.js + indexDB,实现储存和读取模型

3,598 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

一、前言

今天带大家试一下indexDB数据库结合threejs的使用,主要是用dexie操作indexDB数据库来进行模型的储存和读取,项目是在已有的threejs的demo上进行的,关于threejs如何引入和使用,相信小伙伴们都已经很清楚了,本文将不再赘述。

二、实现过程

1、dexie的引入

import Dexie from 'https://unpkg.com/dexie@latest/dist/dexie.js'

// 直接在线引入,也可以下载到本地进行引入,
//vue还可以
npm install dexie
import Dexie from 'Dexie'

2、使用

打开or创建数据库

this.db = new Dexie('threemodel') // 打开or创建数据库

定义好结构 版本数传0.1,代表第一版(0.2就第二版),下面这段代码,将会在threemodel这个数据库下,建立五张表,++表示自增,* 表示多值索引, 本项目只需要其中的model表,其他表小伙伴板看看就行。

  this.db.version(0.1).stores(
    {
      localVersions: 'matadataid, content, lastversionid, date, time',
      users: '++id, name, &username, *email, address.city',
      relations: '++rid, userId1, userId2, [userId1+userId2], relation',
      books: 'id, author, name, *categories',
      model: 'id, name, type, file'
    }
  )
  

定义之后,还需要进行一次数据库操作,才会在indexDB下真正生成,比如我修改model表里的某条信息。

this.blobChange() // 建立完数据库,调用该函数
// 修改model表里的数据
putDataToIndexDB(blob) {
  // 一定要处理成二进制 this.blobChange()返回的是一个blob对象
  console.log(blob)
  this.db.model.put({
    id: 'skyscraperF',
    name: '摩天大夏F',
    type: blob.type,
    file: blob
  })
},

// 这里因为是demo,所以我读取的了本地的一个glb模型进行转换成blob文件流,
// 存储到indexDB库,实际上应该是glb模型应该是从服务端获取到的二进制流,进行本地存储。
blobChange() {
  axios({
    method: 'get',
    url: '../../assets/GLTF-format/skyscraperF.glb',
    responseType: 'blob',
    crossOrigin: true,
    withCredentials: true
  }).then(res => {
    console.log(res.data)
    // 写入indexDB数据库
    this.putDataToIndexDB(res.data)
  })
}

到此创建和写入操作就完成了,来看一下效果吧。

上述操作一共是在IndexDB下建立了一个threemodel的数据库,里面是五张表,model表里,还有我刚写入的数据,file文件就存放着文件的具体信息(以blob二进制形式存放)。

image.png

之后就是获取模型了,为了方便使用,建议把基础的增删改查操作封装一下。

// 如果有多个表,里面的model也可以当成参数传值进去
// add增加,put修改,delete删除,get获取
addDataDB(obj) {
  this.db.model.add(obj)
},
updateDataDB(obj) {
  this.db.model.put(obj)
},
delDataDB(key) { // key一般都是id,这一类唯一标识
  this.db.model.delete(key)
},
/*get方法是获取定义的主键键值为输入数据的第一条数据,如果想获取匹配的所有数据
就得用getAll方法进行获取。不过get、getAll方法需要查询整张表来获得结果,数据
量大的时候效率很低。**所以在日常使用中都是通过添加索引然后使用游标查询索引获
取想要的数据。*/
getDataDB(key) {
  return this.db.model.get(key)
}

我们调用getDataDB方法,看下能否获取到刚刚存的模型。

console.log(this.getDataDB('skyscraperF'))

结果如下

image.png

const modeldata = await this.getDataDB('skyscraperF')
console.log(modeldata)

结果如下

image.png

两个结果的不同,原因是获取数据的过程是异步获取,需要等待,直接console.log,结果是在_value里,这时如果去读_value这个属性也是读不到的(因为返回结果结构已经发生变化),所以建议是await异步等待一下。

这样就能拿到我们刚存的模型了,接下来就是blob生成url,供threejs加载调用了。

// 利用URL.createObjectURL把blob转成load可加载的url
  const modelUrl = URL.createObjectURL(new Blob([modeldata.file]))
  

加载函数代码如下

// 加载模型大楼
async initLoad2() {
  const modeldata = await this.getDataDB('skyscraperF')
  console.log(modeldata)
  // 利用URL.createObjectURL把blob转成load可加载的url
  const modelUrl = URL.createObjectURL(new Blob([modeldata.file]))
  // 加载
  this.gltLoader2 = new THREE.GLTFLoader()
  // this.gltLoader2.load('/assets/GLTF-format/skyscraperF.glb', (gltf) => {
  this.gltLoader2.load(modelUrl, (gltf) => {
    console.log(gltf)
    gltf.scene.position.x = -6
    gltf.scene.position.z = 4
    gltf.scene.children[0].children.forEach((item, i) => {
      gltf.scene.children[0].children[i].castShadow = true
      gltf.scene.children[0].children[i].receiveShadow = true
    })
    gltf.scene.rotateY(0.5 * Math.PI)
    this.scene.add(gltf.scene)
  },
  // 加载进度回调
  (xhr) => {
    console.log(xhr, (xhr.loaded / xhr.total) * 100 + '% loaded')
  }
}

image.png

三、总结

第一次用indexDB,也查了不少资料,感觉使用上还有优化的空间,不过最后还是成功实现了预期效果。下期更新,大模型的压缩方法,希望大家多多支持。

ps: 生活不易,前端叹气。

Snipaste_2022-07-19_15-30-26.jpg