Cesium:地球中实现点击浮动弹窗

我也仅仅是一个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);
    }
  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KaiSarH

如果觉得文章不错,可以支持下~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值