简介面向Vue3开发者的Cesium集成资源包解决在Vue3.x项目中使用Cesium构建三维地球应用时的环境配置与组件封装问题。内容涵盖Vue CLI项目搭建、Webpack别名配置、Cesium安装与模块引入并演示了CesiumViewer组件的创建、生命周期管理、事件绑定及性能优化方法适合正在学习三维GIS开发或需要快速集成Cesium的前端工程师。资源包共85个文件大小约347KB以vue组件、js配置、json清单及html入口等类型为主同时包含完整的vue.config.js和package.json并附有git仓库元数据便于查看项目演进结构。目前已有5121人学习下载。通过阅读示例代码可掌握Cesium与Vue3的组合式写法、视图封装和Vuex/Router联动技巧大幅缩短从零配置到实际应用的开发周期。1. vue3.x Cesium从“能跑”到“能上线”的三维可视化实战我接触 vue3.x Cesium 这个组合时第一反应也是装个包、new 一下 Viewer 就能出地球。结果第一个版本就翻车底图白屏、地形穿模、路由切换回来黑屏光排查问题就耗了三天。后来把整个接入流程理顺才发现 Cesium 在 vue3.x 工程里的坑大多不是引擎本身的问题而是集成方式、静态资源、生命周期这三件事没处理好。这篇笔记面向两类人一类是有 vue 基础、要在业务系统里接入三维地球的前端另一类是正在做数字孪生、园区可视化的团队。目标很具体——让你在 vue3.x 工程里把 Cesium 用起来而且能稳定地上线、后期不返工。2. 环境搭建vue3.x 工程里 Cesium 的初始化与首屏渲染2.1 先解决选型三种集成方式与选型理由Cesium 是一个 WebGL 三维地球引擎官方包自带了一套资源管理和渲染逻辑。vue3.x 组合式 API 的特点是“状态与方法按功能组织”这对 Cesium 其实很友好——因为 Cesium 大量操作是面向对象的命令式调用组合式 API 正好可以把“创建 Viewer、添加图层、事件绑定”收敛到一组复用函数里比 vue2 时代的 mixin 混入模式清楚得多。常见的集成路线有三条。第一条是直接在组件里引入 Cesium模板用 ref 挂容器onMounted 里创建实例优势是简单直接劣势是逻辑散落在各个页面后期难维护。第二条是使用社区封装好的第三方插件这类库能帮你挡住初始化的繁琐步骤但封装的深度往往追不上 Cesium 官方更新节奏遇到自定义需求时容易束手束脚。第三条是自封装组合式函数把 Viewer 和图层操作收敛到独立模块里生产项目我一般主推这条。用一张表格对比三条路线会更直观集成方式上手成本动态切换场景长期维护成本适用场景组件内直接初始化低差容易累积多个实例高逻辑散落临时 Demo第三方封装库低一般看封装是否暴露控制权中版本升级有风险中小型页面组合式函数自封装中好实例统一管理低核心逻辑复用中大型业务系统选择组合式函数做封装本质是把 Cesium 的命令式操作翻译成 vue3.x 的函数组装。比如创建 Viewer 的过程不在每个页面里各写一遍而是收敛到 createViewer 方法里所有页面统一入口。后续 Cesium 主包升级时只需要改这一个入口业务页面代码基本不用动。确定路线后先装依赖。npm install cesium npm install -D vite-plugin-cesium这里装的是 Cesium 核心包和 Vite 集成插件。Cesium 主包的源码里有大量非 ESM 模块直接 import 会报错。vite-plugin-cesium 会自动处理 Cesium 的静态资源、源码转换和打包路径省去手动拷贝 Build 目录、配置公共路径这些繁琐操作。如果不使用这个插件就要自己把 Cesium 包里的 Build/Cesium 目录复制到 public 下并在 index.html 里手工加 script 引用开发环境能跑到生产构建时资源路径就容易出问题所以我一般建议直接用插件。// vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue import cesium from vite-plugin-cesium export default defineConfig({ plugins: [ vue(), cesium() ], server: { port: 3000 } })配置说明vue 插件负责解析 vue3.x 的单文件组件cesium 插件负责处理 Cesium 模块的 ESM 桥接。server.port 是开发服务器端口按团队习惯调整。这个插件的核心价值在于它会把 Cesium 的 Widgets、Assets、Workers 三个资源目录自动处理进构建产物开发和生产环境都能保持一致的行为不用再手动 copy 或担心路径问题。2.2 创建第一个 Viewer容器、参数与生命周期现在开始写第一个组件。vue3.x 中Cesium 的 Viewer 需要一个真实的 DOM 节点作为容器模板里用 ref 标记这个节点等组件挂载完成后再初始化。有一个非常关键的细节Cesium 的 Viewer 在组件卸载时不会自动销毁必须手动调用 destroy 方法否则路由每次切换都会在内存里留下一个 Viewer 实例GPU 资源和 WebGL 上下文会被占满最终页面卡死。先写一个基础组件骨架template div refcesiumContainer classcesium-container/div /template script setup import { ref, onMounted, onBeforeUnmount } from vue import * as Cesium from cesium import { createViewer } from /cesium/createViewer const cesiumContainer ref(null) let viewer null onMounted(() { viewer createViewer(cesiumContainer.value, { imageryProvider: Cesium.createWorldImageryAsync() }) }) onBeforeUnmount(() { if (viewer) { viewer.destroy() viewer null } }) /script style scoped .cesium-container { width: 100%; height: 100vh; position: relative; } /style这段代码的逻辑不复杂模板里声明 div 容器脚本里通过 ref 拿到 DOM 节点onMounted 时创建 VieweronBeforeUnmount 时销毁实例。注意 onBeforeUnmount 里把 viewer 置为 null这一步在 vue3.x 工程里特别关键路由切换不销毁 Viewer控制台多半会陆续出现 WebGL 上下文丢失的警告时间一长页面就会黑屏。createViewer 的封装函数可以长这样// src/cesium/createViewer.js import * as Cesium from cesium export function createViewer(container, options {}) { const viewer new Cesium.Viewer(container, { animation: false, // 关闭动画控件 timeline: false, // 关闭时间轴 baseLayerPicker: false, // 关闭底图选择器 geocoder: true, // 保留搜索框 homeButton: true, // 保留回到初始视角按钮 sceneModePicker: false, // 隐藏 2D/3D 切换控件 navigationHelpButton: false, infoBox: false, // 关闭默认信息面板 selectionIndicator: true, // 保留选择指示器 imageryProvider: options.imageryProvider }) viewer.scene.globe.depthTestAgainstTerrain true return viewer }重要参数说明animation 和 timeline 是 Cesium 自带的时间轴和动画控件业务系统里通常用不到直接关掉能减少对 UI 的干扰。baseLayerPicker 是底图选择器如果业务要求固定底图必须关掉否则用户能随意切换导致风格崩坏。geocoder 搜索框可以留着在处理一个“根据名称定位到设备位置”的需求时官方搜索组件能省不少事。depthTestAgainstTerrain 设为 true 后地形会有真实遮挡关系但这也意味着被地形挡住的建筑模型会看不见后面调试时如果模型莫名消失优先检查这一项。在 vue3.x 组合式 API 的写法下onMounted 和 onBeforeUnmount 配对使用已经很自然。Cesium 实例的生命周期与组件保持一致是避免内存泄漏的第一步后面在第五章工程化部分还会用 store 进一步统一管理。注意Cesium Viewer 创建后不要复制同一个实例给多个组件用多实例同时渲染会消耗大量 GPU 资源。实际项目中尽量通过路由或 v-if 控制唯一地图实例。2.3 底图替换把默认影像层换成业务常用资源开发调试阶段用 Cesium 自带的全球影像服务没毛病但线上项目几乎都要换成业务底图。替换的核心是操作 viewer.imageryLayers 这个图层栈。import * as Cesium from cesium const viewer window.viewer // 移除默认底图 viewer.imageryLayers.removeAll() // 使用 XYZ 标准瓦片服务添加自定义底图 const layer viewer.imageryLayers.addImageryProvider( new Cesium.UrlTemplateImageryProvider({ url: https://{s}.example.com/tile/{z}/{x}/{y}.png, subdomains: [a, b, c], maximumLevel: 18, credit: new Cesium.Credit(第三方瓦片服务) }) ) layer.alpha 1逻辑说明removeAll 清空默认底图addImageryProvider 把新图层压到图层栈顶。UrlTemplateImageryProvider 支持按 URL 模板加载栅格瓦片URL 里的 {z}/{x}/{y} 分别对应缩放级别和瓦片行列号这是 Web GIS 瓦片服务的通用规范。subdomains 可以让 Cesium 把请求分散到 a、b、c 多个子域名缓解并发压力。maximumLevel 设置最大请求层级很多免费瓦片服务在 18 级以后没有数据层级设高了只会白白产生 404。credit 用于显示底图来源版权信息如果影像服务商要求署名这里必须填。如果业务底图是常见 GIS 平台发布的 REST 服务思路一样换成对应的 Provider 类即可。图层顺序上后添加的图层默认显示在最上层如果想在影像之上叠加标注层就再压一层 provider。调试时把 alpha 设为 0.5两层影像半透明叠加可以快速对比两套数据是否对齐。3. 核心功能实战地形、影像与 3D Tiles 的接入3.1 地形加载从默认椭球体到真实高程Cesium 默认的地形是椭球体没有高程起伏这在俯瞰全球场景时够用。但做建筑模型放置、线路规划、淹没分析必须切换为带真实高程的地形服务。import * as Cesium from cesium async function useTerrain() { const terrainProvider await Cesium.createWorldTerrainAsync({ requestVertexNormals: true, // 加载法线数据用于地形光照计算 requestWaterMask: true // 加载水面遮罩河流湖泊显示波纹效果 }) const viewer window.viewer viewer.terrainProvider terrainProvider viewer.scene.globe.depthTestAgainstTerrain true viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees(116.251, 36.859, 3000) }) }参数说明requestVertexNormals 开启后地形表面会有明暗变化山脉沟壑轮廓更真实requestWaterMask 开启后水面出现动态纹理。depthTestAgainstTerrain 设为 true 之后相机视角低于地形高度时会被地形遮挡避免穿模。destination 里的坐标是我做测试用的示例位置替换成你业务真实数据所在地即可。如果项目对数据保密要求高比如政务内网或园区专网不能访问公网的地形服务也可以加载本地发布的地形数据。const provider await Cesium.CesiumTerrainProvider.fromUrl( https://your-server.com/terrain, { requestWaterMask: true, requestVertexNormals: true } ) viewer.terrainProvider providerCesiumTerrainProvider.fromUrl 适用于标准地形瓦片服务。这里有个容易踩的坑本地地形服务如果走 HTTP 协议在 HTTPS 页面里会被浏览器拦截控制台报 mixed content 错误排查时优先看网络面板。提示地形服务地址必须支持跨域请求服务器没有配 CORS 头的话地形数据会一直加载失败这个坑和影像服务的排查思路是一样的。3.2 3D Tiles 模型从加载到裁剪与透明控制3D Tiles 是 Cesium 生态里最重要的三维数据格式。倾斜摄影模型、BIM 模型、手工精模导出的数据最终基本都要转成 3D Tiles 才能在 Cesium 里高效渲染。import * as Cesium from cesium async function loadTileset(viewer, url) { const tileset await Cesium.Cesium3DTileset.fromUrl(url, { maximumScreenSpaceError: 16, // 控制模型在远处和近处的精细度 maximumMemoryUsage: 512, // GPU 缓存上限单位 MB dynamicScreenSpaceError: true, // 视点运动时动态调整渲染精度 cullWithChildrenBounds: true }) viewer.scene.primitives.add(tileset) viewer.zoomTo(tileset) return tileset }参数说明maximumScreenSpaceError 是 3D Tiles 渲染的“桶衡”参数值越小模型在远处也会分割成更多细节画面更精细但性能开销大值越大远处模型越粗糙、帧率越稳。16 是中等偏保守的默认值大场景我一般调到 20 以上。maximumMemoryUsage 限制 GPU 上缓存的数据量512MB 对大多数 Web 应用够用了。dynamicScreenSpaceError 开启后系统会根据相机运动状态自动调整渲染精度用在飞行漫游、车载巡视场景里能让帧率稳定不少。模型挂上去之后高频需求是模型对齐和裁剪。倾斜摄影原始数据一般基于局部工程坐标系把它当作相对位置对齐到 Cesium 的 WGS84 坐标这一步用模型矩阵搞定。const position Cesium.Cartesian3.fromDegrees(116.251, 36.859, 50) const heading Cesium.Math.toRadians(90) // 航向角单位角度转弧度 const pitch Cesium.Math.toRadians(0) // 俯仰角 const roll Cesium.Math.toRadians(0) // 翻滚角 const hpr new Cesium.HeadingPitchRoll(heading, pitch, roll) const fixedFrame Cesium.Transforms.headingPitchRollToFixedFrame( position, hpr, Cesium.Ellipsoid.WGS84 ) tileset.modelMatrix fixedFrame这段代码把模型局部坐标系的零点对齐到指定经纬度并通过 heading、pitch、roll 三个角度控制模型的朝向、俯仰和侧翻。实际项目中如果两个模型之间有位置偏差就在这个矩阵里做平移和旋转校准不要直接改模型原始数据——原始数据改错了还要重新转换代价很大。裁剪操作在二三维一体化项目里几乎必用。倾斜摄影模型经常会拍到红线范围外的杂散建筑和空地用多边形裁剪可以只保留目标区域内的三角网。// 以经纬度坐标定义裁剪多边形 const clippingPolygon Cesium.Cartesian3.fromDegreesArray([ 116.251, 36.859, 116.261, 36.859, 116.261, 36.869, 116.251, 36.869 ]) tileset.clippingPolygons [ new Cesium.ClippingPolygon({ positions: clippingPolygon }) ]注意 fromDegreesArray 里坐标点的排列顺序会影响裁剪方向顺序反了裁剪区域就到了多边形外侧。另外clippingPolygons 只对支持裁剪的 3D Tiles 渲染管线生效如果模型数据格式不是标准的 b3dm裁剪可能无效。透明控制也是常见需求尤其在建筑方案评审时想看建筑内部结构和管线布局常规做法是整体半透明。// 整体半透明 tileset.style new Cesium.Cesium3DTileStyle({ color: rgba(255, 255, 255, 0.6) }) // 恢复不透明 tileset.style new Cesium.Cesium3DTileStyle({ color: color(white) })Cesium3DTileStyle 支持以表达式字符串定义颜色规则rgba 中的第四个通道控制透明度。这个赋值方式是整体生效的性能开销很小可以放心在滑动条联动时频繁调用。3.3 实体与矢量数据点、线、面的动态挂载除了三维模型业务上还经常要在地球上叠加标绘数据比如设备点位、巡检路径、项目范围面。Cesium 里用实体层来做。import * as Cesium from cesium const viewer window.viewer // 添加一个带图标的设备点位 viewer.entities.add({ id: device-001, position: Cesium.Cartesian3.fromDegrees(116.251, 36.859, 120), billboard: { image: /images/device.png, width: 48, height: 48, verticalOrigin: Cesium.VerticalOrigin.BOTTOM // 图标底部对齐坐标点 }, label: { text: 设备A, font: 14px sans-serif, pixelOffset: new Cesium.Cartesian2(0, -30) // 文字相对图标向上偏移 } }) // 添加一条巡检轨迹线 viewer.entities.add({ polyline: { positions: Cesium.Cartesian3.fromDegreesArray([ 116.251, 36.859, 116.262, 36.872, 116.280, 36.866 ]), width: 3, material: new Cesium.ColorMaterialProperty( Cesium.Color.fromCssColorString(#00ff88) ) } })参数说明entities.add 是 Cesium 实体 API 的标准入口id 便于后续按需查找和移除。billboard 的 verticalOrigin 设为 BOTTOM能让图标底部对齐坐标点而不是图标中心压住点位。label 的 pixelOffset 是相对屏幕像素的偏移单位是 CSS 像素用于把标注文字放到图标上方。polyline 的 positions 接收经纬度数组material 配合 ColorMaterialProperty 设置线颜色。动态更新数据时用 viewer.entities.getById(device-001) 取出实体再改 position不要频繁 add 再 remove。大量实体增删会触发场景重建帧率波动明显。正确做法是创建一次实体之后只更新它的 position 和属性字段Cesium 内部会自动处理 diff 更新。4. 避坑指南vue3.x Cesium 高频问题排查与解决4.1 静态资源 404Cesium 的图标和控件样式全丢现象页面容器是黑屏控制台报一堆 404错误路径指向 Widgets/Images 目录下的图片和 CSS 文件找不到。原因Cesium 的构建产物把 UI 资源放在 Build/Cesium/Widgets 目录下vue3.x 工程里如果没用 vite-plugin-cesium这些资源不会自动随工程打包资源路径对不上加载自然失败。解决优先使用 vite-plugin-cesium插件会自动把 Cesium 的 Build 目录资源拷贝到打包产物中。如果因为公司网络策略不允许用第三方插件就手动把 Cesium 包里的 Build/Cesium 目录整体复制到 public 下再在 index.html 里通过 script 引入 Cesium.js 和对应的 css 文件。无论哪种方式检查一下 dist 目录里是否真的存在 Widgets 文件夹。4.2 路由切换后 Viewer 崩溃现象从三维页面切换到列表页再切回来地图变黑屏控制台报Canvas already in use或者 WebGL context lost。原因vue3.x 的路由切换让组件被卸载但 Cesium 的 Viewer 持有的 canvas 和 WebGL 上下文没有释放。第二次进入时浏览器没有回收之前的 GL 上下文新的 Viewer 创建时拿不到可用的 canvas。解决组件卸载时做两件事——调用 viewer.destroy()再把 viewer 引用置 null。如果页面里用 v-if 动态控制三维容器容器移除时 Viewer 也必须一起销毁重新渲染时再 new。我习惯把 Viewer 生命周期封装在独立 hook 里所有页面统一调用就不会漏。4.3 影像图层与地形高程错位现象加载外部影像服务后影像像“盖”在地形上山体处影像明显错开看起来像贴图变形滑落。原因影像服务坐标系与当前场景不一致。Cesium 默认用 WGS84 加墨卡托投影如果影像瓦片用的不是 EPSG:3857 标准需要在 Provider 里指定 tilingScheme。另一个可能原因是影像服务和地形服务的切图原点不一致导致同一经纬度在两层数据上对应不上。解决创建影像 Provider 时显式指定 tilingSchemeconst layer viewer.imageryLayers.addImageryProvider( new Cesium.UrlTemplateImageryProvider({ url: https://{s}.example.com/tile/{z}/{x}/{y}.png, tilingScheme: new Cesium.WebMercatorTilingScheme() }) ) // 手动修正影像偏移 layer.rectangle Cesium.Rectangle.fromDegrees( west offsetLon, south offsetLat, east offsetLon, north offsetLat )手动校准是一种兜底手段治标不治本。生产环境还是要让数据源把坐标系统一到 WGS84 墨卡托每个区域都配一组偏移量会很难维护一旦影像服务更新偏移又得重新测。4.4 vue3.x 响应式数据与 Cesium 事件耦合问题现象在 Cesium 的 screenSpaceEventHandler 回调里修改一个 vue 的 ref 变量界面没有变化控制台也不报错。原因Cesium 的事件回调运行在浏览器原生监听机制上不在 vue 的响应式调度流程内。直接修改 ref 值值变了但不会触发视图更新本质是丢失了 vue3.x 的响应式上下文。解决在 Cesium 事件回调里用 nextTick 包裹或者更规范的做法——让 Cesium 事件只负责产生原始数据把数据交给 Pinia store 或组件内的方法去消费。我自己的习惯是事件回调里不写业务逻辑只往 store 或事件总线里推数据让 vue 侧去处理这样也能避免三维层和业务层之间的隐式依赖。5. 工程化封装用组件 Pinia 管理 Cesium 实例5.1 封装一个可复用的 CesiumMap 组件把 Viewer 的创建、销毁、对外暴露放到一个基础组件里业务页面只负责传入配置和监听回调。下面是基础骨架。template div refcontainer classmap-root/div /template script setup import { ref, onMounted, onBeforeUnmount, provide } from vue import * as Cesium from cesium import { createViewer } from /cesium/createViewer const props defineProps({ imageryProvider: { type: Object, default: null }, terrainProvider: { type: Object, default: null } }) const emit defineEmits([ready, layer-load-error]) const container ref(null) let viewer null onMounted(() { try { viewer createViewer(container.value, { imageryProvider: props.imageryProvider }) provide(cesiumViewer, viewer) // 提供给子孙组件按需取用 emit(ready, viewer) } catch (e) { emit(layer-load-error, e) } }) onBeforeUnmount(() { if (viewer) { viewer.destroy() viewer null } }) defineExpose({ getViewer: () viewer }) /script style scoped .map-root { width: 100%; height: 100%; position: relative; } /style设计思路这个组件把 Viewer 的创建和销毁收敛到一处父组件通过 ready 事件拿到 viewer 引用在业务逻辑里继续操作 Cesium API。error 事件用来捕获加载失败避免白屏无提示。provide 和 defineExpose 的组合让深层嵌套的子组件可以 inject 拿到同一个 viewer 实例这样在左侧树、中间地图、右侧图例的多层布局里任何角落都能安全操作三维场景。props 里不直接暴露全部 Cesium 配置项只给出一两个常用入口避免组件参数膨胀。如果项目里需要大量自定义配置可以在业务组件里通过 getViewer 拿到 viewer 后直接扩展。5.2 Pinia 状态管理让三维状态与业务状态同源Cesium 的很多状态——相机位置、当前选中实体、各图层显隐——本质上是视图状态既影响三维渲染也影响业务面板。一开始我把这些状态直接塞在组件内部很快就发现业务侧的表格、弹窗、查询条件也要根据三维状态变化跨组件通信变得很混乱。后来统一改用 Pinia。// store/cesium.js import { defineStore } from pinia export const useCesiumStore defineStore(cesium, { state: () ({ viewer: null, cameraPosition: null, selectedEntity: null, baseLayer: default, tilesetMap: {} }), actions: { setViewer(viewer) { this.viewer viewer }, setCamera(position) { this.cameraPosition position }, registerTileset(id, tileset) { this.tilesetMap[id] tileset }, removeTileset(id) { delete this.tilesetMap[id] }, flyToModel(id) { const tileset this.tilesetMap[id] if (tileset) { this.viewer.zoomTo(tileset) } } } })使用方式上viewer 在 store 里存放的是引用业务组件不直接操作 Cesium 对象而是调用 store 的 action。例如在工具栏点击“聚焦到模型”组件只需要调用 flyToModel具体的相机飞行逻辑封装在 store 内部对业务侧完全隐藏。这样设计的好处有两个。第一三维场景的操作入口统一页面销毁时 store 可以清楚知道应该清理哪些 tileset 和实体不会出现资源泄漏。第二当业务侧需要读取三维状态快照时——比如当前视角高度、当前选中建筑的类型——直接访问 store 比遍历 Cesium 实例要简单得多也符合 vue3.x 下 useStore 在各组件里自由取用的习惯。状态管理落地之后组件之间不会再为了一个 camera 位置来回传值。第五部分的封装思路是从实践里反复改出来的早期项目里因为状态散乱导致的三维与二维不同步问题在这个结构下很难再发生。6. 点击拾取与坐标转换一个写完就能直接用的交互技巧在 vue3.x Cesium 项目里最常用也最容易写错的交互是“点击地图位置拿到经纬度并拾取该位置上的对象”。Cesium 的坐标体系是三维世界坐标点击事件返回的是屏幕像素坐标中间必须经过两次转换——屏幕坐标转世界坐标世界坐标转经纬度。import * as Cesium from cesium function setupClickHandler(viewer, callback) { const handler new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) handler.setInputAction((movement) { // 第一步屏幕像素坐标 - 世界坐标 Cartesian3 const cartesian viewer.camera.pickEllipsoid( movement.position, viewer.scene.globe.ellipsoid ) if (!cartesian) return // 第二步世界坐标 - 经纬度弧度制 const carto Cesium.Cartographic.fromCartesian(cartesian) // 第三步弧度转角度输出给业务层 const longitude Cesium.Math.toDegrees(carto.longitude) const latitude Cesium.Math.toDegrees(carto.latitude) const height carto.height callback({ longitude, latitude, height, cartesian }) }, Cesium.ScreenSpaceEventType.LEFT_CLICK) return handler }逻辑说明pickEllipsoid 是相机对象上的射线拾取方法返回一个 Cartesian3 世界坐标。carto.height 在这里通常接近 0因为拾取的是椭球面。如果要拾取地形上的点应改用 viewer.scene.pickPosition这会触发 GPU 深度拾取性能消耗略大但拿到的 height 是真实地形高程。要注意 pickPosition 拾取地形时必须满足 depthTestAgainstTerrain 开启否则结果仍然默认落在椭球面上。拾取实体对象是更进阶的用法用户点击一栋楼我们希望拿到 3D Tiles 模型里的具体构件信息。const picked viewer.scene.pick(movement.position) if (Cesium.defined(picked)) { const entity picked.id if (entity instanceof Cesium.Cesium3DTileFeature) { const properties entity.getPropertyNames() const values properties.map((name) ({ name, value: entity.getProperty(name) })) callback({ type: feature, data: values }) } }scene.pick 可能返回 entity、primitive 或 undefined。在倾斜摄影模型上点击时返回的是 Cesium3DTileFeature通过 getPropertyNames 可以拿到该构件挂载的属性——楼层编号、面积、用途、权属人等这些数据可以直接对接业务表单或统计面板。picker 返回的位置如果是 undefined说明点击位置没有命中任何对象需要在业务层做兜底提示不要直接读属性。最后说一个我踩过的坑点击事件处理器在组件里创建后一直留着页面来回切换每进入一次就新增一个 handler结果一次点击被触发了多次。从那以后我每次在组件里创建事件处理器都会在 onBeforeUnmount 里把 handler 调 destroy 干掉保证同一时间只有一个事件监听在跑。这个习惯适用于 vue3.x Cesium 里所有需要 addEventListener 的场景希望也能帮你在项目里少走弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?