一:uniapp中是否可以使用Cesium?

先说结论:
可以,但会存在各种各样的问题和bug ,如果直接是H5没什么大的问题,主要是app,无论是引入还是打包真机模拟都有问题

二:问题的本质和原因

web端如何引入cesium就不说了,单说app端为什么不能直接引入cesium
1.静态引入,也就是在html页面中link标签引入css,script引入cesium的js文件,但是app不依赖html,无论是在本地还是读取线上服务器都不行
2.npm i的方式引入,很抱歉更不行,都知道app不是浏览器是没有window对象的,自然没有浏览器的dom对象
看官网的说法吧
在这里插入图片描述

三:解决方案

那我们要如何引入cesium呢 ,上面说了要使用renderjs来操作,那我们的思路就是在renderjs中动态创建script标签引入cesium的js文件

什么是renderjs 点击访问renderjs地址
在这里插入图片描述

四:代码实现

<template>
	<view class="content">
		<!-- #ifdef APP-PLUS || H5 -->
		<view id="container"></view>
		<!-- #endif -->
		<!-- #ifndef APP-PLUS || H5 -->
		<view>APPH5 环境不支持</view>
		<!-- #endif -->
		<button @click="back"> 按钮</button>
	</view>
</template>
 
<script module="Cesium" lang="renderjs">
	export default {
		data() {
			return {
				domlist: null, // 动态引入dom标签合集
				viewer: null, // 三维视窗
			}
		},
		mounted() {
			this.initResources()
		},
		methods: {
			// 初始化资源,加载后例化cesium
			initResources() {
				if (this.domlist) {
					this.removeResource()
				} else {
					// 动态引入css文件
					const linkDom = document.createElement('link')
					linkDom.rel = "stylesheet"
					 linkDom.href = "https://cesium.com/downloads/cesiumjs/releases/1.84/Build/Cesium/Widgets/widgets.css"
					document.head.appendChild(linkDom)
					var cesiumS = this.requireResources('script',
						'https://cesium.com/downloads/cesiumjs/releases/1.84/Build/Cesium/Cesium.js', this.initCesium)
					this.domlist = [linkDom, 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() {
				let Cesium = globalThis.Cesium 
 let key =
 	"";
 Cesium.Ion.defaultAccessToken = key;

				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; // 显示帧数

			},
		},
		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>

五:遇到的问题,各种离奇Bug

浏览器可以,但是真机模拟或者打包后报错或者白屏

第一种:Failed to execute 'texImage2D' on 'WebGL2RenderingContext': The image element contains cross-origin data, and may not be loaded.
这个意思就是uniapp访问cesium的时候跨域了,有人也给了方法就是修改源码,加一句跨域,也有人巴拉巴拉说了一堆没屁用的话

r.crossOrigin = “anonymous”。

在这里插入图片描述
但是你会发现,你的cesium.js中这方法是被注释掉了,没地方添加,直接说如何解决,本质是你的cesium版本太高,需要降低cesium版本最好是低于1.96版本,测试使用1.84版本即可

第二种:白屏问题Bug

三种可能性引起的

  1. 打包体积超过40M,cesium包体积太大导致无法加载
  2. 云打包别快速打包,直接用传统打包(重点)
  3. 引入本地的cesium的js文件路径不对,也会导致白屏,我也没搞懂这个本地引入路径要怎么整,别人那种引入不生效,搞懂了哥们踢我一下
    在这里插入图片描述

六:剩余的坑

到这里,你的app终于是可以出现球了,但是三维gis老生常谈的问题又来了,性能问题,手机会不会变成暖宝宝,

答案是显然的,而且好多api使用不了,你需要不停的修改源码,但是基础的加载各种数据和点击事件没问题(找了一个专门做uniapp移动端cesium的同学咨询了一下,ps:他们企业都快倒闭了,感谢小伙伴的解惑,预祝下一份工作能发大财)

七:建议

其实移动端要想使用cesium有更好的方案:
1.原生安卓套壳,里面嵌套h5,使用h5实现cesium的各种功能,有大佬已经实现了,感觉才是正途
2.在uniapp中使用web-view嵌套h5,也是可以的,反正真机模拟是可以出现地球并操作的,但是打包没体验过

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐