[cesium]vue3下实现cesium的popup

2,148 阅读1分钟

效果图

效果图

原理

  1. 创建popupContainer,挂载到viewer.cesiumWidget.container
  2. Cesium.SceneTransforms.wgs84ToWindowCoordinates将场景位置转化为屏幕位置
  3. 利用viewer.scene.postRender实时刷新位置
  4. 利用vuerender()h() 渲染自定义组件到 popupContainer

核心代码

import { VNode, render as vue_render, onBeforeUnmount, h } from 'vue';
//cesium类型自己导入
export function useCesiumPopup(getViewer: () => Cesium.Viewer, opts: { className?: string; offset?: [number, number] } = {}) {
const { className, offset = [0, 0] } = opts;

  let _popupContainer: HTMLDivElement | undefined;
  let position: Cartesian3 | undefined;
  let _renderListener: (() => void) | undefined;
  function getPopupContainer() {
    if (_popupContainer) {
      closePopup();
    }
    _popupContainer = document.createElement('div');
    _popupContainer.classList.add('cesium-popup');
    _popupContainer.style.position = 'absolute';
    _popupContainer.style.display = 'block';
    if (className && className !== '') {
      _popupContainer.classList.add(className);
    }
    const _viewer = getViewer();

    _viewer.cesiumWidget.container.appendChild(_popupContainer);
    _renderListener = _viewer.scene.postRender.addEventListener(render);

    return _popupContainer;
  }

  function setContent(vnode: VNode, coord?: Cartesian3) {
    const map_popup = getPopupContainer();
    position = coord;
    vue_render(vnode, map_popup);
  }

  function render() {
    if (!(position && position.x && position.y && _popupContainer)) return;

    const _viewer = getViewer();
    const _position = SceneTransforms.wgs84ToWindowCoordinates(_viewer.scene, position);

    if (_position) {
      _popupContainer.style.left = _position.x - _popupContainer.offsetWidth / 2 + offset[0] + 'px';
      _popupContainer.style.top = _position.y - _popupContainer.offsetHeight + offset[1] + 'px';
    }
  }
  onBeforeUnmount(() => closePopup());

  function closePopup() {
    if (_popupContainer) {
      // const _viewer = getViewer();
      // _viewer.cesiumWidget.container.removeChild(_popupContainer);
      _popupContainer.remove();
      _popupContainer = undefined;
    }
    position = undefined;
    if (_renderListener) {
      _renderListener();
      _renderListener = undefined;
    }
  }

    return {
        setContent,
        closePopup
    };
}

使用

//这里viewer就是声明的变量,用函数是因为viewer是在dom渲染后加载
const { setContent: setCesiumPopup, closePopup: closeCesiumPopup } = useCesiumPopup(() => viewer);
//数据的坐标
const { x, y,z } = sm_geometry.center;
//定位
viewer.camera.flyTo({ destination:  Cesium.Cartesian3.fromDegrees(x, y, 100) });
//渲染到viewer里,PropsPopup是自己的组件,import进来,ts或js写法:
setCesiumPopup(h(PropsPopup, { dataSource: dataSource, onClose: closeCesiumPopup }),  Cesium.Cartesian3.fromDegrees(x, y, z));
//或tsx写法:
setCesiumPopup(<PropsPopup dataSource={dataSource} onClose={closeCesiumPopup} />,Cesium.Cartesian3.fromDegrees(x, y, z))

参考: