Cesium为3dTile模型添加气泡框
在Cesium中没有类似mapbox中的气泡(popup)弹框,在react-hooks中可以封装一个这样的类组件,借助网上提供的思路为外部模型添加popup气泡框,改进后在旋转缩放时仍能保持原位置,不会偏移。
·
前言
在Cesium中没有类似mapbox中的气泡(popup)弹框,在react-hooks中可以封装一个这样的类组件,思路参考来源,在此基础上进行了改进,实现为外部模型添加popup气泡框,在旋转缩放时仍能保持原位置,不会偏移。
基本思路
- 为地图绑定鼠标事件,当拾取到模型时,触发响应函数
- 将鼠标点击位置的屏幕坐标转为笛卡尔坐标,与拾取到的目标模型要素的属性一起传递给popup组件,创建构造函数,触发构造popup类
- 在popup类初始化时,创建div,显示气泡框,同时在该类内部添加每一帧的监听函数,将传递来的笛卡尔坐标转为屏幕坐标,重新设置气泡框位置
- 创建新的气泡框或者关闭气泡框时,移除div元素,解绑监听事件
代码
贴两部分的代码,popup工具类的声明和在主程序中的使用。
import * as Cesium from "cesium";
import './index.css'
interface InfoProrety {
name: string,
viewer: Cesium.Viewer,
properties: PropertyObj,
geometry: Cesium.Cartesian3
}
interface PropertyObj {
[key: string]: string | number;
// important: string;
// other: string;
}
class Popup {
id: number
viewer: Cesium.Viewer
geometry: Cesium.Cartesian3
ctn: HTMLDivElement
eventListener: any
constructor(info: InfoProrety) {
console.log(info)
this.id = 0;
// 展示新的popup时关闭前一个popup
if (document.getElementsByClassName("bx-popup-ctn").length > 0) {
console.log(document.getElementsByClassName("bx-popup-ctn"))
document.getElementsByClassName("bx-popup-ctn")[0].remove()
}
this.viewer = info.viewer; // 弹窗创建的viewer
this.geometry = info.geometry; // 弹窗挂载的位置
this.ctn = document.createElement("div"); // 创建一个div
// classList为html5的新语法,返回元素类名
this.ctn.classList.add("bx-popup-ctn");
this.viewer.container.append(this.ctn); // Cesium.Viewer.container 获取当前viewer的父容器(cesiumContainer)
this.ctn.innerHTML = this.createHtml(info.name, info.properties); //创建Html
this.render(this.geometry);
// 添加监听拖动重新渲染位置 viewer.clock.onTick时刻监听
this.eventListener = this.viewer.clock.onTick.addEventListener(clock => {
this.render(this.geometry);
});
// 关闭按钮绑定关闭事件
document.getElementsByClassName(
"popup-close-button"
// @ts-ignore
)[0].onclick = () => {
this.close();
};
}
//渲染位置
render(geometry: Cesium.Cartesian3) {
const position = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
this.viewer.scene,
geometry
);
if (position) {
this.ctn.style.left = position.x - this.ctn.offsetWidth / 2 + "px";
this.ctn.style.top = position.y - this.ctn.offsetHeight - 30 + "px";
}
}
createHtml(header: string, content: PropertyObj) {
var html =
'<div class="bx-popup-header-ctn">' +
header +
'<span class="popup-close-button" >' + "❌" + '</i></span>' +
"</div>" +
'<div class="bx-popup-content-ctn" >' +
'<div class="bx-popup-content" >' +
this.createTable(content) +
"</div>" +
"</div>" +
'<div class="bx-popup-tip-container" >' +
'<div class="bx-popup-tip" >' +
"</div>" +
"</div>";
return html;
}
createTable(content: PropertyObj) {
let html = '<table class="table-popup">';
for (let key in content) {
html += `<tr><td class="title-popup">${key}</td>
<td class="value-popup">${content[key]}</td></tr>`;
}
html += "</table>";
return html;
}
close() {
this.ctn.remove();
this.viewer.clock.onTick.removeEventListener(this.eventListener);
}
}
export default Popup;
引用部分,只保留相关部分。
const handleChange = (value: string, option: any) => {
console.log(`selected ${value}`, option);
const handler = new Cesium.ScreenSpaceEventHandler(viewer?.scene.canvas)
if (value === 'popup') {
// 添加鼠标事件:右键
handler.setInputAction(function (movement:any) { // 鼠标点击是PositionedEvent类型 :{ position }
// pick方法返回primitive对象,当对象为3d tiles时,返回Cesium3DTileFeature对象
const feature = viewer!.scene.pick(movement.position);
const point = new Cesium.Cartesian2(
movement.position.x,
movement.position.y
)
if (feature instanceof Cesium.Cesium3DTileFeature) {
displaySelectFeature(feature, point)
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
};
const displaySelectFeature = (feature: Cesium.Cesium3DTileFeature, point: Cesium.Cartesian2) => {
if (!Cesium.defined(feature)) {
return;
}
// 获取当前鼠标位置三维坐标的一般流程(无模型):
// 1:通过camera的getPickRay,将当前的屏幕坐标转为ray(射线)=> viewer.camera.getPickRay(windowCoordinates);
// 2:找出ray和地形的交点,得出三维世界坐标 scene.globe.pick(ray, scene);
// 获取当前鼠标位置三维坐标的一般流程(有模型):
// viewer!.scene.pickPosition(Cartesian2)
let propertyObj: PropertyObj = {} // 存储到对象中
const propertyIds = feature.getPropertyIds()
propertyIds.length && propertyIds.forEach(item => {
propertyObj[item] = feature.getProperty(item)
});
// @ts-ignore
PopupRef.current = new Popup({
name: 'xxx',
viewer: viewer!,
properties: propertyObj,
geometry:viewer!.scene.pickPosition(point)
});
}
总结
借助网上的思路实现的一个popup功能,核心的部分其实两篇来源中都已经实现了,修正了下存在的问题。
更多推荐
所有评论(0)