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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM