Cesium 基础:地球场景初始化与视角控制

在 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,可直接克隆运行,也可基于此扩展更多功能(如标绘、图层管理、轨迹回放等)。

相关推荐
椰羊~王小美1 小时前
前后端 格式化货币的方法
java·前端
苯酸氨酰糖化物1 小时前
HTML+CSS学信网学籍学历查询页面-支持任意修改内容信息
前端·css3·html5·娱乐
幻云20101 小时前
Next.js 之道:从入门到精通
前端·javascript·vue.js·人工智能·python
2501_944521592 小时前
Flutter for OpenHarmony 微动漫App实战:标签筛选功能实现
android·开发语言·前端·javascript·flutter
EndingCoder2 小时前
构建工具集成:Webpack 和 TypeScript
前端·webpack·typescript
卡西里弗斯奥2 小时前
【Tomcat】部署Web服务器之Tomcat
服务器·前端·tomcat
Sheldon一蓑烟雨任平生2 小时前
Sass 星空(Sass + keyframes 实现星空动画)
前端·css·vue3·sass·keyframes
⑩-2 小时前
VUE3学习
前端·javascript·vue.js
Mr Xu_2 小时前
Vue 3 中使用 mitt 实现组件间通信的实践与解析
前端·javascript·vue.js