在 WebGIS 开发领域,Cesium 是一款功能强大的 3D 地球可视化库,结合 Vue3 + TypeScript 可以高效构建交互式的地球场景应用。本文将详细讲解如何基于 Vue3 封装 Cesium 组件,实现地球场景初始化、自定义视角控制工具栏(飞至指定城市、缩放、旋转、定位等),并整合 Element Plus 提升界面交互体验。
一、项目环境准备
1. 核心依赖
- Vue3 + TypeScript:基础开发框架,提供组件化开发能力
- Cesium:3D 地球可视化核心库
- Element Plus:Vue3 端组件库,用于快速构建工具栏
- Vite:构建工具(本文示例基于 Vite 构建,需配置 Cesium 环境变量)
2. 环境配置
在 .env 文件中配置 Cesium Ion Token(需前往 Cesium 官网申请):
bash
VITE_CESIUM_ION_TOKEN=你的Cesium Ion Token
二、核心组件封装:CesiumViewerControl.vue
该组件是整个地球场景的核心,包含「场景容器」「工具栏」「Cesium 初始化逻辑」「视角控制方法」四部分。
1. 模板结构(Template)
模板分为地球渲染画布和工具栏两部分,工具栏通过 Element Plus 的 el-button-group 实现按钮分组,提升视觉层级:
html
<template>
<div class="viewer-control">
<!-- Cesium 地球渲染容器 -->
<div class="viewer-control__canvas" ref="container"></div>
<!-- 视角控制工具栏 -->
<div class="viewer-control__toolbar">
<el-button-group>
<el-button size="small" @click="flyBeijing">飞至北京</el-button>
<el-button size="small" @click="flyShanghai">飞至上海</el-button>
<el-button size="small" @click="resetView">重置视角</el-button>
</el-button-group>
<el-button-group class="viewer-control__group">
<el-button size="small" @click="zoomIn">放大</el-button>
<el-button size="small" @click="zoomOut">缩小</el-button>
<el-button size="small" @click="rotateLeft">左旋</el-button>
<el-button size="small" @click="rotateRight">右旋</el-button>
<el-button size="small" type="primary" @click="locate">定位</el-button>
</el-button-group>
</div>
</div>
</template>
2. 脚本逻辑(Script Setup + TS)
(1)基础导入与变量声明
导入 Vue3 生命周期、Cesium 核心库及样式,声明容器引用和 Cesium Viewer 实例:
TypeScript
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue'
import * as Cesium from 'cesium'
import 'cesium/Build/Cesium/Widgets/widgets.css'
// 地球容器 DOM 引用
const container = ref<HTMLDivElement | null>(null)
// Cesium Viewer 实例(核心对象)
let viewer: Cesium.Viewer | undefined
</script>
(2)通用视角飞行方法封装
封装 flyTo 方法,实现「传入经纬度 + 高度,让相机飞至指定位置」的通用逻辑,统一控制飞行时长、视角倾角等参数:
TypeScript
const flyTo = (lon: number, lat: number, height = 30000) => {
if (!viewer) return
// 将经纬度 + 高度转换为 Cesium 笛卡尔坐标
const destination = Cesium.Cartesian3.fromDegrees(lon, lat, height)
viewer.camera.flyTo({
destination,
orientation: {
heading: viewer.camera.heading, // 保持原有航向
pitch: Cesium.Math.toRadians(-25), // 视角倾角(向下25度)
roll: 0,
},
duration: 1.5, // 飞行时长(秒)
})
}
(3)具体视角控制方法
基于通用 flyTo 方法,实现「飞至北京 / 上海」「重置视角」「缩放」「旋转」「定位当前位置」等功能:
TypeScript
// 飞至北京(固定经纬度)
const flyBeijing = () => flyTo(116.4074, 39.9042)
// 飞至上海(固定经纬度)
const flyShanghai = () => flyTo(121.4737, 31.2304)
// 重置视角(返回 Cesium 默认主页视角)
const resetView = () => {
if (!viewer) return
viewer.camera.flyHome(1.2)
}
// 放大视角(基于当前高度的 30% 缩放,最小缩放距离 1000 米)
const zoomIn = () => {
if (!viewer) return
const pos = Cesium.Cartographic.fromCartesian(viewer.camera.position)
viewer.camera.zoomIn(Math.max(pos.height * 0.3, 1000))
}
// 缩小视角(逻辑同放大,方向相反)
const zoomOut = () => {
if (!viewer) return
const pos = Cesium.Cartographic.fromCartesian(viewer.camera.position)
viewer.camera.zoomOut(Math.max(pos.height * 0.3, 1000))
}
// 左旋(每次旋转 10 度)
const rotateLeft = () => {
if (!viewer) return
const step = Cesium.Math.toRadians(10)
viewer.camera.setView({
orientation: {
heading: viewer.camera.heading + step,
pitch: viewer.camera.pitch,
roll: viewer.camera.roll,
},
})
}
// 右旋(逻辑同左旋,方向相反)
const rotateRight = () => {
if (!viewer) return
const step = Cesium.Math.toRadians(10)
viewer.camera.setView({
orientation: {
heading: viewer.camera.heading - step,
pitch: viewer.camera.pitch,
roll: viewer.camera.roll,
},
})
}
// 定位当前位置(基于浏览器地理定位 API)
const locate = () => {
if (!viewer || !navigator.geolocation) return
navigator.geolocation.getCurrentPosition(
(pos) => {
const { longitude, latitude } = pos.coords
flyTo(longitude, latitude, 15000) // 定位后飞至当前位置,高度 15000 米
},
() => {
resetView() // 定位失败则重置视角
},
{ enableHighAccuracy: true, timeout: 10000 } // 高精度定位,超时 10 秒
)
}
(4)生命周期管理
onMounted:初始化 Cesium Viewer,配置 Token、地形、场景参数;onBeforeUnmount:销毁 Viewer 实例,释放内存,避免内存泄漏。
TypeScript
onMounted(async () => {
// 配置 Cesium Ion Token
const token = import.meta.env.VITE_CESIUM_ION_TOKEN as string | undefined
if (token) {
Cesium.Ion.defaultAccessToken = token
}
// 初始化 Viewer 实例
if (container.value) {
viewer = new Cesium.Viewer(container.value, {
animation: false, // 隐藏动画控件
timeline: false, // 隐藏时间轴
baseLayerPicker: true, // 显示图层选择器
sceneModePicker: true, // 显示场景模式切换器(2D/3D/Columbus View)
geocoder: true, // 显示地理编码搜索框
homeButton: true, // 显示主页按钮
navigationHelpButton: false, // 隐藏导航帮助按钮
terrainProvider: undefined, // 先不设置地形,后续异步加载
})
// 异步加载全球地形数据
try {
const terrain = await Cesium.createWorldTerrainAsync()
viewer.terrainProvider = terrain
} catch {}
// 开启地形深度检测(提升 3D 地形交互精度)
viewer.scene.globe.depthTestAgainstTerrain = true
}
})
onBeforeUnmount(() => {
// 销毁 Viewer 实例,释放资源
if (viewer && !viewer.isDestroyed()) {
viewer.destroy()
viewer = undefined
}
})
3. 样式(Style Scoped)
通过定位让工具栏悬浮在地球画布上方,保证交互性的同时不遮挡核心视图:
html
<style scoped>
.viewer-control {
width: 100%;
height: 100vh;
position: relative;
}
.viewer-control__canvas {
width: 100%;
height: 100%;
}
.viewer-control__toolbar {
position: absolute;
top: 12px;
left: 12px;
display: flex;
gap: 8px;
z-index: 10; // 保证工具栏在最上层
}
.viewer-control__group {
margin-left: 8px;
}
</style>
三、组件挂载与全局配置
1. 全局引入 Element Plus
在 main.ts 中全局注册 Element Plus,确保按钮组件正常渲染:
TypeScript
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
2. 根组件使用 Cesium 组件
在 App.vue 中引入并使用 CesiumViewerControl 组件,作为应用入口:
TypeScript
<script setup lang="ts">
import CesiumViewerControl from './components/CesiumViewerControl.vue'
</script>
<template>
<CesiumViewerControl />
</template>
<style scoped>
</style>
四、代码亮点与核心价值
1. 组件化封装
将 Cesium 初始化、视角控制、资源销毁等逻辑封装在独立组件中,符合 Vue3 组件化思想,便于复用和维护。
2. 通用方法抽象
提取 flyTo 通用飞行方法,统一管理视角飞行的参数(时长、倾角、高度),避免重复代码。
3. 优雅的资源管理
通过 onMounted 初始化、onBeforeUnmount 销毁 Viewer 实例,解决 Cesium 内存泄漏问题。
4. 友好的交互体验
- 结合 Element Plus 构建美观的工具栏,按钮分组清晰;
- 地理定位失败时自动降级为重置视角,提升容错性;
- 缩放逻辑基于当前高度动态计算,避免缩放过度 / 不足。
5. 可扩展的配置
Viewer 初始化参数可根据需求灵活调整(如隐藏 / 显示各类控件、切换地形数据源等)。
五、项目仓库
完整代码已开源至 Gitee:cesium-vue3-demo,可直接克隆运行,也可基于此扩展更多功能(如标绘、图层管理、轨迹回放等)。