效果图
原理
- 创建
popupContainer,挂载到viewer.cesiumWidget.container下 Cesium.SceneTransforms.wgs84ToWindowCoordinates将场景位置转化为屏幕位置- 利用
viewer.scene.postRender实时刷新位置 - 利用
vue的render()和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))
参考: