#1)背景
最近在做结合 uni-app 和 Cesium.js 的webgis开发项目,希望达到写一套前端代码应用(适配)到不同端(web,android,ios,小程序等)的项目。遇到一些问题,记录下来加强记忆,也希望能帮助到一些同学。下面罗列一些背景和工具:
框架介绍:什么是uni-app?; 第三方库:Cesium.js ;
目的:主要用 Cesium 做一个三维GIS系统,并使其可以在其他移动端运行 ;
开发工具:HBuilderX;
#2)遇到的问题
web端引入Cesium.js相关文件有多种方法:
- 静态引入。在html中用script引入js文件、link链接css文件;(app端无法使用,app不依赖html模板)
- npm安装Cesium模块并用import导入;(app端无法使用,需要使用window设置全局对象CESIUM_BASE_URL,app没有window)
- 动态创建script标签引入js文件;(app端无法使用,app没有document)
上述方法均无法将Cesium.js及其相关配套文件引入程序中
#3)问题分析与解决方案
主要问题在于app端不能使用浏览器自带对象,比如document、window、localstorage、cookie等,更不能使用jquery等依赖这些浏览器对象的框架。事实上uni-app官网也有相关说明:区别于传统 web 开发的注意
但是Cesium的使用必须使用document或者window等浏览器对象,怎么办呢?如图:
使用 uni-app 提供的 renderjs 方式
#4)主要过程
试错过程就不赘述了,以下主要是使用 renderjs 方式实现在web端和安卓app端的过程。
(1)使用开发工具HBuilderX创建uniapp项目,选择默认模板
(2)html和css,设置 Cesium 实例化容器
<template>
<view class="content">
<!-- #ifdef APP-PLUS || H5 -->
<view id="container"></view>
<!-- #endif -->
<!-- #ifndef APP-PLUS || H5 -->
<view>非 APP、H5 环境不支持</view>
<!-- #endif -->
</view>
</template>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#container {
width: 100%;
height: 500px;
}
</style>
(3)设置 script 节点的 lang 为 renderjs ,通过 renderjs 引入 Cesium.js
<script module="Cesium" lang="renderjs">
export default {
data() {
return {
viewer: null
}
},
mounted() {
/**
* 引入js文件
*/
},
methods: {
init(){}
}
}
</script>
注意:script标签的 module 和 lang 都必须设置,否则app端可能无法显示
(4)动态引入较大类库避免影响页面展示
mounted() {
// 动态引入css文件
const linkDom = document.createElement('link')
linkDom.rel = "stylesheet"
linkDom.href = "static/Cesium/Widgets/widgets.css"
document.head.appendChild(linkDom)
// 动态引入较大类库避免影响页面展示
const script = document.createElement('script')
// view 层的页面运行在 www 根目录,其相对路径相对于 www 计算
script.src = 'static/Cesium/Cesium.js'
script.onload = this.init.bind(this)
document.head.appendChild(script)
},
然后就可以在初始化函数 init() 中使用Cesium了
(5)cesium初始化(照顾部分没有做过地图开发的同学,这里把cesium实例化的函数放上来)
methods: {
init() {
// console.log(window.Cesium.VERSION) // APP可运行
Cesium.Ion.defaultAccessToken =
'TOKEN'; // token自己在cesium官网申请一下
this.viewer = new Cesium.Viewer('container', {
terrainProvider: Cesium.createWorldTerrain(), // 创建世界地形
})
}
}
效果:
PS 完整代码
<template>
<view class="content">
<!-- #ifdef APP-PLUS || H5 -->
<view id="container"></view>
<!-- #endif -->
<!-- #ifndef APP-PLUS || H5 -->
<view>非 APP、H5 环境不支持</view>
<!-- #endif -->
</view>
</template>
<script module="Cesium" lang="renderjs">
export default {
data() {
return {
domlist: null, // 动态引入dom标签合集
viewer: null, // 三维视窗
tilesetData: null, // 三维数据
moduleHeight: 0 // 模型距离地表高度
}
},
mounted() {
this.initResources()
},
methods: {
// 初始化资源,加载后例化cesium
initResources() {
if (this.domlist) {
this.removeResource()
} else {
// 动态引入css文件
const linkDom = document.createElement('link')
linkDom.rel = "stylesheet"
linkDom.href = "static/Cesium/Widgets/widgets.css"
// linkDom.href = "https://cesium.com/downloads/cesiumjs/releases/1.84/Build/Cesium/Widgets/widgets.css"
document.head.appendChild(linkDom)
var datS = this.requireResources('script', 'static/dat.gui.js')
var cesiumS = this.requireResources('script', 'static/Cesium/Cesium.js', this.initCesium)
// var cesiumS = this.requireResources('script',
// 'https://cesium.com/downloads/cesiumjs/releases/1.84/Build/Cesium/Cesium.js', this.initCesium)
this.domlist = [linkDom, datS, cesiumS]
}
},
// 动态创建script引用第三方类库
requireResources(dom, src, callback) {
// 动态引入较大类库避免影响页面展示
const script = document.createElement(dom)
// view 层的页面运行在 www 根目录,其相对路径相对于 www 计算
script.src = src;
document.head.appendChild(script);
callback && (callback instanceof Function) && (script.onload = callback.bind(this));
return script
},
// 移除动态添加的标签,避免重复添加标签
removeResource() {
if (this.domlist)
for (let i = 0; i < this.domlist.length; i++) {
document.head.removeChild(this.domlist[i])
}
},
// 初始化
initCesium() {
// console.log(window.Cesium.VERSION) // APP可运行
Cesium.Ion.defaultAccessToken =
'';
this.viewer = new Cesium.Viewer('container', {
terrainProvider: Cesium.createWorldTerrain(), // 创建世界地形
geocoder: false, //右上角 搜索
// homeButton: false, //右上角 Home
// sceneModePicker: false, //右上角 2D/3D切换
// baseLayerPicker: false, //右上角 地形
navigationHelpButton: false, //右上角 Help
animation: false, // 左下角 圆盘动画控件
timeline: false, //时间轴
fullscreenButton: false, //右下角 全屏控件
vrButton: false, // 如果设置为true,将创建VRButton小部件。
scene3DOnly: true // 每个几何实例仅以3D渲染以节省GPU内存
// infoBox: false //隐藏点击要素后的提示信息
})
this.viewer._cesiumWidget._creditContainer.style.display = 'none'; //隐藏版本信息
this.viewer.scene.debugShowFramesPerSecond = true; // 显示帧数
// 请求3dtiles数据
this.tilesetData = new Cesium.Cesium3DTileset({
url: 'http://localhost:9099/wuhan_baimo/tileset.json'
});
// 数据请求完成后加载
this.tilesetData.readyPromise
.then((tileset) => {
this.viewer && this.viewer.scene.primitives.add(tileset);
this.viewer.zoomTo(tileset);
})
.otherwise(function(error) {
console.log(error);
});
},
},
beforeDestroy() {
this.removeResource()
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#container {
width: 100%;
height: 500px;
/* background: #0ff; */
}
</style>