我也仅仅是一个Cesium的初学者,代码参考了小专栏里面的cesium 之自定义气泡窗口以及社区- Cesium中文网Cesium中级教程4 - 空间数据可视化的两篇文章写得.
其实如果不自己定义弹窗,直接在原生弹窗的description 里定义自己的窗口也是可以的,不过用起来不如自己定义一个方便,所以我就采用自己定义的,在需要的时候弹出自己的隐藏原生的窗口就可以了.
因为项目的原因,我只贴出来核心代码.不过把这些代码看懂了,复制粘贴,改一改地球的div名称,也就可以用了.^ ^后面是我对这段代码的理解
①css
.leaflet-popup {
position: absolute;
text-align: center;
}
.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
padding: 4px 4px 0 0;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.leaflet-popup-content-wrapper {
text-align: center;
max-height: 200px;
overflow-y: auto;
background: white;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 19px;
line-height: 1.4;
}
.leaflet-popup-tip-container {
/*修改弹窗位置*/
margin: 0 auto;
width: 200px;
height: 50px;
position: relative;
overflow: hidden;
}
.leaflet-popup-tip {
background: white;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
②JavaScript
var content;
var infoDiv = '<div id="trackPopUp" style="display:none;">'+
'<div id="trackPopUpContent" class="leaflet-popup" style="top:5px;left:0;">'+
'<a class="leaflet-popup-close-button" href="#">×</a>'+
'<div class="leaflet-popup-content-wrapper">'+
'<div id="trackPopUpLink" class="leaflet-popup-content" style="max-width:100%;"></div>'+
'</div>'+
'<div class="leaflet-popup-tip-container">'+
'<div class="leaflet-popup-tip"></div>'+
'</div>'+
'</div>'+
'</div>';
// 首先构造一个存放弹框的div,方便设置
$("#cesiumContainer").append(infoDiv);
// 在cesiumContainer后插入弹框div
var handler3D = new Cesium.ScreenSpaceEventHandler(tdViewer.scene.canvas);
// ScreenSpaceEventHandler 处理用户输入事件。可以添加自定义功能以在以下位置执行当用户输入输入时。
handler3D.setInputAction(function(movement) {
// 监听鼠标的当前位置坐标,然后根据当前坐标去动态更新气泡窗口div的显示位置;
var pick = tdViewer.scene.pick(movement.position);
// ovement.position是获取屏幕坐标
// pick 记录当前屏幕位置
if(pick && pick){
// 这里的判断条件还是蛮有用的,比如你点击某些点的时候想弹出自定义弹窗,其他点弹出原生弹窗,就需要在这里进行判断了
$("#trackPopUp").show();
// 显示弹窗容器
var cartographic = Cesium.Cartographic.fromCartesian(pick.primitive._actualPosition);
// 获取点的经纬度
var point=[cartographic.longitude / Math.PI * 180, cartographic.latitude / Math.PI * 180];
// 转换坐标
var destination=Cesium.Cartesian3.fromDegrees(point[0], point[1], 3000.0);
// destination是我们点击之后,flyto的位置
content =
'<table border="1" width="400px" colspan="4">'+
' <thead ><th colspan="4" style="text-align: center">' + geo_json_data[0].灾害点名称 + '</th></thead>'+
' <tbody>'+
' <tr >'+
' <th align="left">地理位置:' + geo_json_data[pick.id._code].灾害点位置 + '<br/>管道桩号:' + geo_json_data[pick.id._code].桩号 + '<br/>灾害类型:' + geo_json_data[pick.id._code].地质灾害类型 + '<br/>风险等级:' + geo_json_data[pick.id._code].风险等级 + '</th>'+
' <th align="center"><img src="' + '凯尔特人.jpg' + ' " alt="灾害点图片加载失败" width="100px" height="100px"></th>'+
' </tr>'+
' </tbody>'+
' <tfoot >'+
' <td colspan="4" align="center">'+
' <button style="color: #003da8">基本特征</button>'+
' <button style="color: #003da8" οnclick="this">勘察设计</button>'+
' <button style="color: #003da8">巡检卡</button>'+
' <button style="color: #003da8">历年对比</button>'+
' <button style="color: #003da8">巡查上报</button></td>'+
' </tfoot>'+
'</table>';
// content是核心,你想弹出的东西,就全部放在这里面
var obj = {position:movement.position,destination:destination,content:content};
// 构造一个参数,包括事件、 位置、已经弹框
infoWindow(obj);
function infoWindow(obj) {
var picked = tdViewer.scene.pick(obj.position);
// 首先获取点击点的信息
if (Cesium.defined(picked)) {
// 判断 如果点被定义了
var id = Cesium.defaultValue(picked.id, picked.primitive.id);
// 获取id(id就是原生弹窗的标题)
if(id ){
if (obj.destination) {
back_position = new Cesium.Cartesian3.fromDegrees(getCenterPosition().x, getCenterPosition().y, getCenterPosition().z);
// 我在这里用back_position记录的点击之前的位置,便于×掉弹窗后返回
tdViewer.camera.flyTo({
// 跳转到我们刚才定义的位置
destination: obj.destination
});
}
// 填充内容
$(".cesium-selection-wrapper").show();
// cesium-selection-wrapper是cesium内置的东西
$('#trackPopUpLink').empty();
// empty() 方法从被选元素移除所有内容,包括所有文本和子节点。
$('#trackPopUpLink').append(obj.content);
// append() 方法在被选元素的结尾(仍然在内部)插入指定内容。
function positionPopUp(c) {
var x = c.x - ($('#trackPopUpContent').width()) / 2;
var y = c.y - ($('#trackPopUpContent').height());
// 为所有匹配元素(#trackPopUpContent)设置transform的值为 'translate3d(' + x + 'px, ' + y + 'px, 0)'
$('#trackPopUpContent').css('transform', 'translate3d(' + x + 'px, ' + y + 'px, 0)');
}
var c = new Cesium.Cartesian2(obj.position.x, obj.position.y);
$('#trackPopUp').show();
positionPopUp(c);
//实时更新位置
var removeHandler = tdViewer.scene.postRender.addEventListener(function () {
var changedC = Cesium.SceneTransforms.wgs84ToWindowCoordinates(tdViewer.scene, pick.primitive._actualPosition);
// 我们转动地球,也会实时更新弹窗的位置.并不会一成不变
if (c && changedC && c.x && changedC.x && c.y && changedC.y) {
if ((c.x !== changedC.x) || (c.y !== changedC.y)) {
positionPopUp(changedC);
c = changedC;
}
}
});
// PopUp close button event handler
$('.leaflet-popup-close-button').click(function () {
$('#trackPopUp').hide();
$('#trackPopUpLink').empty();
$(".cesium-selection-wrapper").hide();
removeHandler.call();
tdViewer.camera.flyTo({
// 回到我们点击前的位置
destination: back_position
});
return false;
});
}
}
}
}
else{
$('#trackPopUp').hide();
}
function getCenterPosition() {
//获取当前位置
var result = tdViewer.camera.pickEllipsoid(new Cesium.Cartesian2(tdViewer.canvas.clientWidth / 2, tdViewer.canvas
.clientHeight / 2));
var curPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(result);
var lon = curPosition.longitude * 180 / Math.PI;
var lat = curPosition.latitude * 180 / Math.PI;
var height = getHeight();
return {
x: lon,
y: lat,
z: height
};
}
function getHeight() {
//获取当前高度
if (tdViewer) {
var scene = tdViewer.scene;
var ellipsoid = scene.globe.ellipsoid;
var height = ellipsoid.cartesianToCartographic(tdViewer.camera.position).height;
return height;
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
我在js代码里做了比较详细的注解,有一部分是之前代码的我直接copy过来了
在我看来,这段其实最重要的是3D和2D的坐标转换以及定位问题,还有弹窗的位置实时更新,Cesium实在太强大了,很多东西已经封装好了,我们直接拿来用就可以了.
这段代码直接复制到程序里可能是没法跑起来的,需要改一些参数.你可以试着读一下代码,整体还是比较清晰的,如果你有什么问题,可以留言给我.
运行效果:
补充:
如果你不喜欢上面直接定义table的,也可以自己定义div生成弹窗
我的理解:其实弹框是content,放在预先定义的infoDiv.
如果content改成下面这样,然后加上下面的css就OK了
content =
`<div id="DisasterPointMapView" class="div-popup" >
<div class="div-popup-main">
<div class="div-popup-title">${ pick.id.properties._灾害点名称._value}</div>
<div class="div-popup-content">
<div class="div-popup-content-left">
<div class="div-popup-item" style="border-top: 0px solid">
<span class="div-popup-left">地理位置</span>
<span class="div-popup-right">${ pick.id.properties._灾害点位置._value}</span>
</div>
<div class="div-popup-item">
<span class="div-popup-left">管道桩号</span>
<span class="div-popup-right">${ pick.id.properties._桩号._value}</span>
</div>
<div class="div-popup-item">
<span class="div-popup-left">灾害类型</span>
<span class="div-popup-right">${ pick.id.properties._地质灾害类型._value}</span>
</div>
<div class="div-popup-item">
<span class="div-popup-left">风险等级</span>
<span class="div-popup-right">${ pick.id.properties._风险等级._value}</span>
</div>
<div class="div-popup-item">
<span class="div-popup-left">调查时间</span>
<span class="div-popup-right">${ pick.id.properties._调查时间._value}</span>
</div>
</div>
<div class="div-popup-content-right">
<img src="/Images/account/1.png"/>
</div>
</div>
<div class="div-popup-bottom">
<a class="div-popup-bottom-item" href="#" οnclick="DisasterPointMapViewClick(1)"><img src="/Img/home/map-icon/基本特征.png"/><span>基本特征</span></a>
<a class="div-popup-bottom-item" href="#" οnclick="DisasterPointMapViewClick(2)"><img src="/Img/home/map-icon/勘查.png"/><span>勘察设计</span></a>
<a class="div-popup-bottom-item" href="#" οnclick="DisasterPointMapViewClick(3)"><img src="/Img/home/map-icon/巡检卡.png"/><span>巡检卡</span></a>
<a class="div-popup-bottom-item" href="#" οnclick="DisasterPointMapViewClick(4)"><img src="/Img/home/map-icon/历年对比.png"/><span>历年对比</span></a>
<a class="div-popup-bottom-item" href="#" οnclick="DisasterPointMapViewClick(5)"><img src="/Img/home/map-icon/巡查上报.png"/><span>巡查上报</span></a>
</div>
</div>
</div>`;
.div-popup {
text-align: center;
max-width: 45vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
top: 20vh;
left: 25%;
z-index: 100;
}
.div-popup-arrow {
width: 22px;
height: 12px;
}
.div-popup-main {
background: rgba(14,36,111,0.85);
border-radius: 4px;
width: 100%;
color: #ffffff;
}
.div-popup-title {
font-size: 16px;
font-family: PingFang SC;
font-weight: 400;
color: #ffffff;
padding: 10px 0;
}
.div-popup-content {
display: flex;
border-top: 2px solid;
border-bottom: 2px solid;
margin: 0px 15px;
}
.div-popup-item {
border-top: 2px solid;
display: flex;
}
.div-popup-left {
font-size: 14px;
font-family: PingFang SC;
font-weight: 400;
border-right: 2px solid;
padding: 10px 15px;
}
.div-popup-right {
flex: 2;
font-size: 14px;
font-family: PingFang SC;
font-weight: 400;
padding: 10px 15px;
text-align: left;
}
.div-popup-content-left {
flex: 6;
}
.div-popup-content-right {
border-left: 2px solid;
padding: 5px;
flex: 2;
}
.div-popup-content-right img {
width: 100%;
height: 100%;
min-width: 150px;
max-width: 250px;
}
.div-popup-bottom {
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
padding: 0px 30px;
}
.div-popup-bottom-item {
background: rgba(0,101,196,1);
/* border-radius:4px; */
border-top-left-radius: 4px;
border-top-right-radius: 4px;
padding: 5px;
display: flex;
justify-content: center;
align-items: center;
margin: 0px 5px;
text-decoration-line: none;
width: 85px;
overflow: hidden;
word-break: keep-all
}
.div-popup-bottom-item:hover {
text-decoration-line: none;
}
.div-popup-bottom-item img {
width: 20px;
height: 20px;
margin-right: 10px;
}
.div-popup-bottom-item span {
font-size: 13px;
font-family: PingFang SC;
font-weight: 400;
color: rgba(255,255,255,1);
}