• vue3使用cesium实现跟随弹框


     本文最终效果是实现移动地图位置后,点弹框会跟随点一起移动

    测试项目目录结构如下:

     

     vite.config.ts代码如下:

    1. import { fileURLToPath, URL } from 'node:url'
    2. import cesium from 'vite-plugin-cesium'; // 引入插件
    3. import { defineConfig } from 'vite'
    4. import vue from '@vitejs/plugin-vue'
    5. // https://vitejs.dev/config/
    6. export default defineConfig({
    7. plugins: [vue(), cesium()],
    8. resolve: {
    9. alias: {
    10. '@': fileURLToPath(new URL('./src', import.meta.url))
    11. }
    12. }
    13. })

     初始化地图 initCesium.js 代码如下

    1. import { onMounted } from 'vue';
    2. import { Viewer } from 'cesium';
    3. import * as Cesium from "cesium";
    4. export let viewer;
    5. export let handler;
    6. export function initCesium() {
    7. onMounted(() => {
    8. viewer = new Viewer('cesiumContainer', {
    9. timeline: false,
    10. fullscreenButton: false,
    11. shouldAnimate: true,
    12. geocoder: false,
    13. sceneModePicker: false,
    14. baseLayerPicker: false,
    15. homeButton: false,
    16. navigationHelpButton: false,
    17. selectionIndicator: false,
    18. //skyBox: false,
    19. infoBox: false,
    20. // 实现canvas缓存获得canvas图像内容
    21. contextOptions: {
    22. webgl: { preserveDrawingBuffer: true },
    23. },
    24. });
    25. viewer.camera.flyTo({
    26. destination: Cesium.Cartesian3.fromDegrees(108.09876, 37.200787, 1400000),
    27. duration: 1,
    28. });
    29. handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    30. addLayer();
    31. })
    32. const addLayer = () => {
    33. const tdt_tk = "天地图的tk"; //一天只能请求一万次啊
    34. let TDTImgProvider = new Cesium.WebMapTileServiceImageryProvider({
    35. url: `http://t0.tianditu.gov.cn/vec_w/wmts?tk=${tdt_tk}`,
    36. layer: "vec",
    37. style: "default",
    38. format: "tiles",
    39. tileMatrixSetID: "w",
    40. maximumLevel: 18,
    41. });
    42. let TDTZJProvider = new Cesium.WebMapTileServiceImageryProvider({
    43. url: `http://t0.tianditu.gov.cn/cva_w/wmts?tk=${tdt_tk}`,
    44. layer: "cva",
    45. style: "default",
    46. format: "tiles",
    47. tileMatrixSetID: "w",
    48. maximumLevel: 18,
    49. });
    50. viewer.imageryLayers.addImageryProvider(TDTImgProvider);
    51. viewer.imageryLayers.addImageryProvider(TDTZJProvider);
    52. }
    53. }

     地图相关操作 useCesium.js 代码如下:

     监听事件建议必须清除,否则系统会出现卡顿问题

    1. import { viewer, handler } from '@/hooks/initCesium';
    2. import * as Cesium from "cesium";
    3. import { ref, nextTick } from 'vue';
    4. export function useCesium() {
    5. const popFlag = ref(false); // 弹框显示
    6. const popData = ref(); // 点击数据
    7. const mapPopup = ref(); // 弹框dom
    8. const setPoint = (points) => {
    9. points.forEach((e) => {
    10. addpoint(e)
    11. })
    12. setEvent();
    13. }
    14. // 添加点
    15. const addpoint = (
    16. e
    17. ) => {
    18. const imgUrl = new URL('../assets/yl.png', import.meta.url).href;
    19. viewer.entities.add({
    20. id: e.id,
    21. data: e,
    22. position: Cesium.Cartesian3.fromDegrees(e.position[0], e.position[1]),
    23. billboard: {
    24. image: imgUrl,
    25. width: 36,
    26. height: 48,
    27. }
    28. });
    29. };
    30. // 添加绑定事件
    31. const setEvent = () => {
    32. // 左键点击事件
    33. let leftclick = Cesium.ScreenSpaceEventType.LEFT_CLICK;
    34. viewer.screenSpaceEventHandler.removeInputAction(leftclick);
    35. handler.setInputAction((movement) => {
    36. // 返回笛卡尔2坐标系 - 为点击点位置
    37. // 获取点击的实体
    38. const pick = viewer.scene.pick(movement.position);
    39. if (!pick || !pick.id) {
    40. return false;
    41. }
    42. const pick_obj = Cesium.defaultValue(pick.id, pick.primitive.id);
    43. // 判断是否是Cesium实体
    44. if (pick_obj instanceof Cesium.Entity) {
    45. // 经纬度转笛卡尔3
    46. const cartesian3 = Cesium.Cartesian3.fromDegrees(
    47. Number(pick_obj.data.position[0]),
    48. Number(pick_obj.data.position[1]),
    49. 0.1
    50. );
    51. // 获取实体笛卡尔2坐标系
    52. const screenposition = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
    53. viewer.scene,
    54. cartesian3
    55. );
    56. // 这里使用实体的坐标而不是用点击点的坐标,为了防止弹框位置相对于点位置不固定
    57. createPopwinOnMap(pick_obj.data, screenposition);
    58. }
    59. }, leftclick)
    60. }
    61. // 存放监听事件
    62. const closePopEvent = [];
    63. // 弹窗相关
    64. const createPopwinOnMap = async (
    65. value,
    66. clickPostion
    67. ) => {
    68. // popFlag.value = false;
    69. popData.value = value;
    70. popFlag.value = true;
    71. await nextTick();
    72. if (mapPopup.value) {
    73. // 获取根节点
    74. const domref = mapPopup.value.$el;
    75. if (!domref) {
    76. return;
    77. }
    78. const position = viewer.scene.camera.pickEllipsoid(
    79. clickPostion,
    80. viewer.scene.globe.ellipsoid
    81. );
    82. let c = new Cesium.Cartesian2(clickPostion.x, clickPostion.y);
    83. //球面转动后 UI跟随物体处理
    84. let temEvent = viewer.scene.postRender.addEventListener(() => {
    85. if (position && c) {
    86. const changedC = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
    87. viewer.scene,
    88. position
    89. );
    90. domref.style.bottom = viewer.canvas.clientHeight - c.y + "px";
    91. domref.style.left = c.x - 100 + "px";
    92. if (changedC) {
    93. c = changedC;
    94. }
    95. }
    96. });
    97. closePopEvent.push(temEvent);
    98. }
    99. };
    100. // 清楚弹框监听事件
    101. const removeEvent = () => {
    102. if (closePopEvent.length > 0) {
    103. closePopEvent.forEach((item) => {
    104. // 执行监听事件本身,可删除监听事件
    105. item();
    106. });
    107. }
    108. closePopEvent.length = 0;
    109. };
    110. const closeMapPopup = () => {
    111. popFlag.value = false;
    112. removeEvent();
    113. };
    114. return { addpoint, setPoint, popFlag, popData, mapPopup, closeMapPopup }
    115. }

     index.vue代码如下:

    1. <script setup lang='ts'>
    2. import { initCesium } from '@/hooks/initCesium';
    3. import { useCesium } from '@/hooks/useCesium';
    4. import iconPop from '@/components/iconPop.vue'
    5. initCesium()
    6. const { setPoint, popFlag, popData, mapPopup, closeMapPopup } = useCesium();
    7. const addPagePoint = () => {
    8. const points = [
    9. {
    10. id:'0000',
    11. name: '我是0',
    12. position: [108.09876, 37.200787]
    13. },
    14. {
    15. id:'1111',
    16. name: '我是1',
    17. position: [106.398901, 33.648651]
    18. },
    19. {
    20. id:'2222',
    21. name: '我是2',
    22. position: [113.715685, 37.845557]
    23. },
    24. {
    25. id:'333',
    26. name: '我是3',
    27. position: [113.09876, 33.200787]
    28. },
    29. ]
    30. setPoint(points)
    31. }
    32. script>
    33. <style lang='scss' scoped>
    34. .index {
    35. width: 100%;
    36. height: 100%;
    37. position: fixed;
    38. #cesiumContainer {
    39. position: fixed;
    40. top: 0;
    41. bottom: 0;
    42. right: 0;
    43. left: 0;
    44. }
    45. .buttonBox {
    46. position: fixed;
    47. z-index: 9;
    48. right: 0;
    49. }
    50. }
    51. style>

    弹框组件 iconPop.vue,内容如下: 

    1. <template>
    2. <div class="mapPop" v-if="popupData">
    3. <div class="mapHeader">
    4. <span>{{ popupData ? popupData.name : '测试' }}span>
    5. <span @click="close">xspan>
    6. div>
    7. <div class="mapContent">
    8. div>
    9. div>
    10. template>
    11. <script setup>
    12. import { watch } from 'vue';
    13. const props = defineProps({
    14. popupData: {}
    15. })
    16. watch(() => props.popupData, (val) => {
    17. console.log(val);
    18. })
    19. const emit = defineEmits(['closePop'])
    20. // 关闭
    21. const close = () => {
    22. emit('closePop')
    23. }
    24. script>
    25. <style lang="scss" scoped>
    26. .mapPop {
    27. z-index: 2;
    28. transform: translate(-20px, -40px);
    29. position: absolute;
    30. width: 240px;
    31. height: 165px;
    32. background: rgba(255, 255, 255);
    33. box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.16);
    34. display: flex;
    35. flex-direction: column;
    36. z-index: 999;
    37. &::before {
    38. content: "";
    39. position: absolute;
    40. display: inline-block;
    41. top: 100%;
    42. border: solid transparent;
    43. content: " ";
    44. height: 0;
    45. width: 0;
    46. position: absolute;
    47. pointer-events: none;
    48. border-top-color: white;
    49. border-width: 10px;
    50. left: 120px;
    51. margin-left: -10px;
    52. }
    53. .mapHeader {
    54. width: 100%;
    55. height: 40px;
    56. background-color: #67C23A;
    57. display: flex;
    58. align-items: center;
    59. padding: 0 16px;
    60. box-sizing: border-box;
    61. justify-content: space-between;
    62. &.unComplate {
    63. background-color: #F4921D;
    64. }
    65. span:first-child {
    66. font-size: 16px;
    67. color: #FFFFFF;
    68. }
    69. .el-icon-close {
    70. font-size: 20px;
    71. color: #FFFFFF;
    72. cursor: pointer;
    73. }
    74. }
    75. .mapContent {
    76. padding: 20px 0 0 16px;
    77. box-sizing: border-box;
    78. width: 100%;
    79. height: 1px;
    80. flex: 1;
    81. p {
    82. font-size: 14px;
    83. line-height: 20px;
    84. color: #595959;
    85. &:not(:last-child) {
    86. margin-bottom: 10px;
    87. }
    88. }
    89. }
    90. }
    91. style>

     效果如下,点击右上角画点按钮,进行点绘制,点击地图图标点,出现弹框

  • 相关阅读:
    pandas的一些函数
    19.0、C语言——指针笔试面试题
    python openai playground使用教程
    获取鼠标在画布中的位置
    十二、Docker的简介
    Python基础与应用代码
    【LeetCode】187. 重复的DNA序列
    Java集合(一)
    【力扣hot100】刷题笔记Day17
    C++ 多态(补充)
  • 原文地址:https://blog.csdn.net/weixin_45122120/article/details/128181425