leaflet中的事件穿透

在总结方法之前,先总结一下问题。

用过leaflet的人都知道,leaflet中有这样一个方法:

L.geoJSON(data, {
    style: function (feature) {
        return {color: feature.properties.color};
    }
}).bindPopup(function (layer) {
    return layer.feature.properties.description;
}).addTo(map);

这个方法可以将封装好的GeoJson直接转化为FeatureGroup,非常方便。

GeoJson 的 featureType 有多种,这里拿 Point 和 Polygon 两类来举例说明,它们在 leaflet 中是两个代表。Point 类型的 geojson 数据在转化为地图图层的渲染方式是 marker,本质上是 div,也就是说,如果你的 geojson 里有 1 万条数据,那它就会在地图上创建 1 万个 div,很恐怖有木有,事实上也是不可取,因为一口气创建一万个 div 还是带来了很大的工作量,一般配置的电脑上运行这样的页面会卡出翔;再说 Polygon 多边形类型,Polygon  类型的 geojson 数据在转化为地图图层的渲染方式是 Canvas,这与 marker 有本质上的区别,即使你的 geojson 里有 1 万条数据,它也只会在其所在的 Pane 上创建 1 个 Canvas标签,页面地图流畅程度丝毫不受影响。 

说到这里,其实离本文的问题已经跑远了,但说这些也是为了更好的理解这个问题或者说现象。div 是有层级关系(zIndex)的,而且这种关系是非常容易被控制的,任何时候我们都可以很轻易去改变,那么 Canvas 呢?

在 leaflet 的使用中可能会碰到这样的情况,在同一层的 Pane 上,比如在 “ overlayPane ” 上,同时定义了以三角形为主和以四边形为主的 Polygon 类型的 FeatureGroup,因此最后渲染出的页面应该是这两个 Feature Group 的内容展示在同一个 Canvas 标签中,假如它们分别都绑定了各自的popup事件,而恰好它们在地图上的区域有所交叉和重叠(假设三角形在四边形的上方,即三角形覆盖了四边形的重叠区域),那这下子就尴尬了,当点击地图上它们的重叠区域时,这个问题的高潮也就来了。

从理论上无非四种结果:触发三角形的popup;触发四边形的popup;二者都没触发;二者都触发了。

而从测试后的事实的表象来看,无论是哪儿的重叠区域,被触发的,永远是下方被覆盖的四边形。

一开始我是懵比的,我觉得这是一个诡异的bug,一个问题的发生找不到合乎逻辑的解释。寥寥数行代码我反复看的心疲力乏,有一句话最能描述我当时的心境,理智让我怀疑一切!

好了,不兜圈子了,这个bug神秘的面纱下,其实就是事件穿透,事实的本质是最后一种预想,二者都触发了。leaflet 中 popup 有个特性,整个地图中有且只能有一个 popup 会被触发,事情的真相是,当鼠标点击它们的重叠区域时,首先会触发位于上方的三角形,紧接着触发下方的四边形。触发四边形的过程中会解除掉三角形的触发状态。这就是真相,一个典型的事件穿透问题。

搞清楚了问题的来龙去脉,解决问题的思路就一下打开了,可能稍微棘手点的就是,popup事件都是绑定在同一个canvas中的每一个单元上。

直接上代码,后面不想再多说了,还是要留下一些思考的空间。

var level_active = false;
var ship_active = false;
/*=============================================== (实时)通航等级 ====================================================*/
function getPassLevelLayer() {
    if (grade_cache == "") {
        return;
    }
    levelLayer = L.geoJson(grade_cache, {
        style: function (feature) {
            return {color: feature.properties.color, weight: weightValueArray[map.getZoom()], pane: 'overlayPane'};
        },
        onEachFeature: function (feature, layer) {

            layer.bindPopup(eachFeaturePopup(feature));
            layer.on("mouseover",function(){
                level_active = true;
            }).on("mouseout",function () {
                level_active = false;
            }).on('click',function () {
                if(level_active&&ship_active){
                    ship_popup_obj.openPopup();
                }
            });
        }
    });
    return levelLayer;
}
/*===================================================== 船 舶 ========================================================*/
function loadShipSoapLayer() {
    if (shipSoapLayer == null) {
        shipSoapLayer = L.geoJson(ship_soap_cache, {
            style: function (feature) {
                return {
                    fillColor: feature.properties.Color,
                    fillOpacity: 1,
                    color: "black",
                    weight: 1,
                    pane: 'overlayPane'
                }; 
            },
            onEachFeature: function (feature, layer) {
                layer.bindPopup(eachShipFeaturePopup(feature), {
                    closeButton: false,
                    className: "ShipSoap",
                    Ship_Id: feature.properties.ShipId
                }).on('popupopen ', function (e) {
                    if(level_active&&ship_active){
                        ship_popup_obj = e.target;
                    }
                    var shipid = map._popup.options.Ship_Id;
                    $('.ShipSoap-Foot_Btn').on('click', function () {
                        window.layer.open({
                            type: 2,
                            skin: 'layui-layer-hei',
                            title: '通航查询',
                            shadeClose: false,
                            shade: [0.9, '#6b6b6b'],
                            maxmin: false,
                            area: ['100%', '100%'],
                            content: 'ship.html&shipId=' + shipid
                        });
                    })
                }).on('popupclose', function () {
                    $('.ShipSoap-Foot_Btn').unbind();
                }).on("mouseover",function(){
                    ship_active = true;
                }).on("mouseout",function () {
                    ship_active = false;
                });
            }
        }).setZIndex('9999');
    }
    return shipSoapLayer;
}
原文地址:https://www.cnblogs.com/unique1319/p/7813858.html