项目模型过大导致内网环境下加载速度慢,用户体验不佳。由于浏览器无法直接读取本地文件,我们尝试使用Electron框架发布桌面端应用,以便通过Node访问本地文件。然而,用户仍希望在Web端使用。为此,我们考虑利用浏览器的缓存、sessionStorage和localStorage,但它们存在存储限制。最终,选择了使用IndexedDB进行数据存储
项目结构
核心代码
顺着路径去找到这个文件 检索processArrayBuffer将代码加入
function isEmpty(obj) {
return Object.keys(obj).length === 0;
}
function setArrayBufferObj(name, arrayBuffer) {
window.bufferList[name] = arrayBuffer
console.log(window.bufferList, "添加")
}
if (isEmpty(window.arrayBufferList)) {
arrayBuffer = await requestPromise;
if (arrayBuffer) {
setArrayBufferObj(tile._contentHeader.uri, arrayBuffer)
}
} else {
let uri = tile._contentHeader.uri
arrayBuffer = window.arrayBufferList[uri]
}
直接上代码
main.js
import { createApp } from 'vue'
import './style.css'
import "cesium/Build/Cesium/Widgets/widgets.css";
import App from './App.vue'
window.CESIUM_BASE_URL = '/Cesium';
// createApp(App).mount('#app')
function loadModuleScript() {
createApp(App).mount('#app')
}
let indexedDBVision = Number(window.localStorage.getItem("iDBV"));
console.log(indexedDBVision);
if (indexedDBVision < 1) {
indexedDBVision = 1;
}
window.arrayBufferList = {};
let indexedDB = null;
const dbName = "MODELCATCHE";
const objectStoreName = "product";
// 打开一个 IndexedDB 数据库,指定数据库名称和版本号
const req = window.indexedDB.open(dbName, indexedDBVision);
// 监听数据库打开成功事件
req.onsuccess = function (event) {
// 当数据库成功打开时输出信息
console.log("数据库打开成功");
// 获取打开的数据库对象
indexedDB = event.target.result;
// 检查对象仓库是否存在
if (!indexedDB.objectStoreNames.contains(objectStoreName)) {
// 如果对象仓库不存在,加载模块脚本
loadModuleScript();
console.log("对象仓库不存在,添加对象仓库及初始数据");
// 设置下一个版本号
const version = indexedDB.version + 1;
// 将新版本号存入 localStorage
window.localStorage.setItem("iDBV", version);
// 关闭当前数据库连接
indexedDB.close();
// 再次打开数据库,使用新版本号
const secondReq = window.indexedDB.open(dbName, version);
// 处理数据库升级事件
secondReq.onupgradeneeded = function (event) {
const db = event.target.result;
// 创建一个对象仓库,指定主键
const objectStore = db.createObjectStore(objectStoreName, {
keyPath: "item",
});
// 向对象仓库中添加初始数据,初始化为空数组
objectStore.add({ item: "item", data: {} });
};
// 监听数据库成功打开的事件
secondReq.onsuccess = function (event) {
// 初始化全局变量
window.bufferList = {};
// 获取新打开的数据库对象
indexedDB = event.target.result;
// 创建一个读写事务
const transaction = indexedDB.transaction([objectStoreName], "readwrite");
// 获取对象仓库
const objectStore = transaction.objectStore(objectStoreName);
// 在这里继续处理业务逻辑
};
} else {
// 如果对象仓库已存在,创建一个读写事务
const transaction = indexedDB.transaction([objectStoreName], "readwrite");
// 获取对象仓库
const objectStore = transaction.objectStore(objectStoreName);
// 从对象仓库获取数据
const getDataReq = objectStore.get("item");
// 监听获取数据成功事件
getDataReq.onsuccess = function (event) {
// 如果成功获取到数据
if (getDataReq.result) {
// 将获取到的数据赋值给全局变量
window.arrayBufferList = getDataReq.result.data;
window.bufferList = {};
console.log("已有数据:", getDataReq.result.data);
// 加载模块脚本
loadModuleScript();
// 在这里处理已有数据
} else {
// 如果没有数据,添加初始数据
console.log("没有数据,添加初始数据");
objectStore.add({ item: "item", data: {} }); // 初始化数据为空数组
window.bufferList = {};
loadModuleScript(); // 加载模块脚本
}
};
}
};
req.onerror = function (event) {
console.log("打开数据库时出错:", event.target.error);
};
App.vue
<template>
<div id="app">
<div style="position: absolute;left: 40px;top: 40px;z-index: 999;color: blue;"> {{ loadingText }}</div>
<div style="width: 100%;height: 100%;" id="ces"></div>
<!-- <div class="background-video-container">
<video autoplay muted loop class="background-video">
<source src="/0828_0.mp4" type="video/mp4">
</video>
<div class="content">
<slot></slot>
</div>
</div> -->
</div>
</template>
<script setup>
import * as Cesium from "cesium";
import { onMounted, ref } from 'vue'
const loadingText = ref('')
function createViewer() {
const CesiumToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3YTBjZmUwYi1lNzg3LTRiY2UtYTBmZi00NzJmY2E3MzhkZDAiLCJpZCI6MjM1NTU2LCJpYXQiOjE3MjQxMzkyNzZ9.1xQWtg4OFt2mv9rHql3sbxyd0mQyfTjNLcRTw1a6PDk";
// const earthImageryProvider = await Cesium.SingleTileImageryProvider.fromUrl(earthImg);
Cesium.Ion.defaultAccessToken = CesiumToken;
window.viewer = new Cesium.Viewer("ces", {
infoBox: false,
geocoder: false, // 位置查找工具
homeButton: false, // 视角返回初始位置
sceneModePicker: false, // 选择视角的模式(球体、平铺、斜视平铺)
baseLayerPicker: false, // 图层选择器(地形影像服务)
navigationHelpButton: false, // 导航帮助(手势,鼠标)
animation: false, // 左下角仪表盘(动画器件)
timeline: true, // 底部时间线
fullscreenButton: false, // 全屏
vrButton: false, // VR
selectionIndicator: false,
shouldAnimate: true,
});
load3dModel()
}
async function load3dModel() {
// 从指定的 URL 加载 3D tileset
let Tileset = await Cesium.Cesium3DTileset.fromUrl(
url,
{
// 缓存设置以提高加载性能
cacheBytes: Number.MAX_VALUE,
maximumCacheOverflowBytes: Number.MAX_VALUE,
}
);
// 将 tileset 添加到 Cesium 视图器的场景中
window.viewer.scene.primitives.add(Tileset);
// 将视图器缩放以适应 tileset
window.viewer.zoomTo(Tileset);
// 添加一个事件监听器,用于监控 tileset 的加载进度
Tileset.loadProgress.addEventListener(function (loaded, total) {
// 从 localStorage 中获取 iDBVval,以确定加载状态
let iDBVval = Number(window.localStorage.getItem("iDBVval"));
// 根据 iDBVval 值更新加载文本
loadingText.value = iDBVval === 0
? "正在下载模型,请稍等片刻"
: "正在加载模型,请稍等片刻";
// 检查加载是否完成
if (loaded === 0 && total === 0) {
// 如果这是第一次加载,提示保存
if (iDBVval === 0) {
loadingText.value = "模型保存中...";
let indexedDBVision = Number(window.localStorage.getItem("iDBV"));
const dbName = "MODELCATCHE";
// 打开 IndexedDB
const req = window.indexedDB.open(dbName, indexedDBVision);
req.onsuccess = function (event) {
const indexedDB = event.target.result; // 获取数据库对象
const transaction = indexedDB.transaction(["product"], "readwrite"); // 创建事务
const objectStore = transaction.objectStore("product"); // 获取对象存储
console.log(window.bufferList);
// 用模型数据更新对象存储
const updateRequest = objectStore.put({
item: "item",
data: window.bufferList,
});
updateRequest.onsuccess = function () {
loadingText.value = "模型保存成功"; // 成功时更新加载文本
// 获取已保存的数据以确认
const getDataReq = objectStore.get("item");
getDataReq.onsuccess = function () {
if (getDataReq.result) {
window.localStorage.setItem("iDBVval", 1); // 更新 localStorage 状态
loadingText.value = "模型加载完成"; // 更新加载文本
// 延迟以便于视觉反馈
setTimeout(() => {
console.log(getCurrentTime());
}, 1000);
}
};
};
updateRequest.onerror = function () {
loadingText.value = "模型保存失败"; // 失败时更新加载文本
};
};
} else {
// 如果之前已经加载,只需确认加载完成
loadingText.value = "模型加载完成";
// 延迟以便于视觉反馈
setTimeout(() => {
console.log(getCurrentTime());
}, 1000);
}
}
});
}
onMounted(() => {
createViewer()
})
function getCurrentTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
</script>
<style scoped>
#app {
width: 100vw;
height: 100vh;
overflow: hidden;
}
.background-video-container {
position: relative;
width: 100%;
height: 100vh;
/* 全屏视频 */
overflow: hidden;
}
.background-video {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
object-fit: cover;
/* 确保视频覆盖整个背景 */
transform: translate(-50%, -50%);
z-index: 1;
/* 视频置于底层 */
}
.content {
position: relative;
z-index: 2;
/* 页面内容放置于视频上层 */
color: #ffffff;
/* 确保内容颜色在视频背景上清晰可见 */
}
</style>
需要注意的地方
1.缓存模型的地址必须和当时储存时的地址一样,需要替换模型的话就要手动清理数据库和缓存,可以自己优化一下我这里只说如何本地缓存
2.启动命令需要加上 --force这里不做解释自行百度
"dev": "vite --force",
3.鼠标缩放查看模型时有时会导致模型一部分切片重新请求导致缺失,最好是在加载时加入loading一是防止用户操作,二是为了提高数据储存成功的概率,在模型加载事件完成后加入这个属性就可以防止模型缺失的问题
本人比较愚钝目前只能这样解决,有哪位大神有更好的办法希望能沟通交流一下