Vue3 + Cesium 实战:给你的地图广告牌(Billboard)加一个会‘跟跑’的弹窗(附完整代码)

张开发
2026/4/21 10:58:28 15 分钟阅读

分享文章

Vue3 + Cesium 实战:给你的地图广告牌(Billboard)加一个会‘跟跑’的弹窗(附完整代码)
Vue3 Cesium 实战打造智能跟随的3D地图弹窗组件当我们在三维地理信息系统中展示点位信息时静态弹窗往往会因为地球旋转而飘走。想象一下用户点击纽约自由女神像的标记后当地图旋转到亚洲视角时信息框却停留在太平洋上空——这种体验显然不够专业。本文将带你用Vue3和Cesium实现一个会跟跑的智能弹窗让信息始终精准锚定在目标位置。1. 环境准备与基础架构在开始编码前我们需要搭建好开发环境。推荐使用Vite作为构建工具它能完美支持Vue3和Cesium的现代开发需求。npm create vitelatest cesium-vue-app --template vue-ts cd cesium-vue-app npm install cesium cesium/engine types/cesium --saveCesium的引入需要特殊配置。在vite.config.ts中添加import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], optimizeDeps: { exclude: [cesium/engine] } })创建基础组件结构!-- CesiumViewer.vue -- template div idcesium-container div v-ifactiveBillboard classbillboard-popup :stylepopupStyle click.stopclosePopup !-- 弹窗内容将通过Teleport动态注入 -- slot namepopup :billboardDataactiveBillboard.data / /div /div /template2. 核心跟随逻辑实现弹窗跟随的核心在于实时计算屏幕坐标。Cesium提供了scene.postRender事件它会在每一帧渲染前触发是更新弹窗位置的理想时机。// 在setup函数中 const updatePopupPosition () { if (!activeBillboard.value) return const scene viewer.scene const position activeBillboard.value.position const canvasPosition Cesium.SceneTransforms.wgs84ToWindowCoordinates( scene, position ) if (canvasPosition) { popupStyle.value { left: ${canvasPosition.x - popupWidth / 2}px, top: ${canvasPosition.y - popupHeight}px, display: block } } } onMounted(() { viewer.scene.postRender.addEventListener(updatePopupPosition) }) onUnmounted(() { viewer.scene.postRender.removeEventListener(updatePopupPosition) })性能优化要点只在有激活的广告牌时执行坐标计算使用防抖技术避免频繁的样式更新在组件卸载时及时移除事件监听3. 响应式弹窗内容管理Vue3的Composition API让我们可以优雅地管理弹窗状态。我们创建一个可复用的useBillboardPopup组合式函数export function useBillboardPopup(viewer: Viewer) { const activeBillboard refBillboardEntity|null(null) const popupVisible ref(false) const showPopup (billboard: BillboardEntity) { activeBillboard.value billboard popupVisible.value true } const closePopup () { popupVisible.value false activeBillboard.value null } // 添加点击事件处理器 const setupClickHandler () { const handler new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) handler.setInputAction((movement: any) { const picked viewer.scene.pick(movement.position) if (picked?.id?.billboard) { showPopup({ position: picked.id.position.getValue(viewer.clock.currentTime), data: picked.id.properties?.getValue(Cesium.JulianDate.now()) }) } }, Cesium.ScreenSpaceEventType.LEFT_CLICK) return () handler.destroy() } return { activeBillboard, popupVisible, showPopup, closePopup, setupClickHandler } }4. 高级功能扩展基础跟随功能实现后我们可以添加更多增强体验的特性4.1 平滑过渡动画通过CSS Transition实现弹窗的平滑出现/消失.billboard-popup { transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55); transform-origin: bottom center; [data-stateentering], [data-stateexiting] { opacity: 0; transform: scale(0.9) translateY(10px); } }4.2 自适应内容布局根据视口位置自动调整弹窗方向const calculatePopupDirection () { if (!activeBillboard.value) return bottom const position activeBillboard.value.position const cameraPosition viewer.camera.position const direction Cesium.Cartesian3.subtract( position, cameraPosition, new Cesium.Cartesian3() ) const dot Cesium.Cartesian3.dot( direction, viewer.camera.right ) return dot 0 ? left : right }4.3 性能监控与优化添加性能统计面板实时监控帧率viewer.cesiumWidget.creditContainer.style.display none viewer.scene.debugShowFramesPerSecond true对于大量广告牌场景建议使用实例化渲染(InstancedBillboardCollection)实现视锥体裁剪(View Frustum Culling)采用LOD(Level of Detail)技术5. 工程化实践与组件封装将完整功能封装为可复用的Vue组件script setup langts import { useBillboardPopup } from ./composables/useBillboardPopup const props defineProps{ viewer: Cesium.Viewer popupWidth?: number popupHeight?: number }() const { activeBillboard, popupVisible } useBillboardPopup(props.viewer) /script template Teleport to#cesium-container div v-ifactiveBillboard popupVisible classbillboard-popup :stylepopupStyle div classpopup-arrow :data-directionpopupDirection / div classpopup-content slot namecontent v-bindactiveBillboard.data / /div /div /Teleport /template配套的TypeScript类型定义interface BillboardEntity { position: Cesium.Cartesian3 data: Recordstring, any } interface BillboardPopupProps { viewer: Cesium.Viewer popupWidth?: number popupHeight?: number maxWidth?: number animationDuration?: number }在真实项目中这样的组件可以轻松集成到各种GIS应用中template CesiumViewer readyonViewerReady / BillboardPopup v-ifviewer :viewerviewer template #content{ data } h3{{ data.title }}/h3 p{{ data.description }}/p img v-ifdata.image :srcdata.image altLocation preview / /template /BillboardPopup /template6. 常见问题与调试技巧弹窗位置抖动问题确保在postRender事件中使用Cesium.SceneTransforms.wgs84ToWindowCoordinates而非已废弃的cartesianToCanvasCoordinates检查CSS中是否使用了会触发重排的属性如transform内存泄漏预防onUnmounted(() { viewer.scene.postRender.removeEventListener(updatePopupPosition) clickHandler?.destroy() })跨域资源加载 在vite配置中添加CESIUM_BASE_URL环境变量// vite.config.ts export default defineConfig({ define: { process.env.CESIUM_BASE_URL: JSON.stringify(/node_modules/cesium/engine) } })调试工具推荐Cesium Inspectorviewer.extend(Cesium.viewerCesiumInspectorMixin)Chrome的Layer面板检查复合层Performance面板记录性能快照在最近的一个智慧城市项目中这套方案成功支持了超过5000个动态标记点的流畅展示。关键发现是当弹窗内容复杂时使用CSS will-change属性提前告知浏览器哪些属性会变化能显著提升渲染性能.billboard-popup { will-change: transform, opacity; }

更多文章