Leaflet 自定義Popup彈窗

首先感謝 Leaflet - 自定義彈出框(popup)提供的擴展

但是實際結合組件化的使用過程中,對於彈窗內元素的高寬存在不確定性(即初始化時不傳入容器的寬高),所以需要進一步對Popup組件進行拓展,以便適應更自由的窗體組件

稍微解讀了下Leaflet對於popup組件的源代碼,其實popup對於整體的地圖容器來說,也只是地圖上根據像素位置定位的元素之一,因此對其的控制也和其它圖層一樣,對初始化、更新、層級變化做監聽,修改位置和樣式即可,以下是代碼(ES5)

// 重寫setTransform,由於不再固定寬度,所以增加translateX(-50%)水平居中
function setTransform(el, offset, scale) {
  var pos = offset || new L.Point(0, 0);
  el.style[L.DomUtil.TRANSFORM] =
    (L.Browser.ie3d
      ? "translate(" + pos.x + "px," + pos.y + "px,0) translateX(-50%)"
      : "translate3d(" + pos.x + "px," + pos.y + "px,0) translateX(-50%)") +
    (scale ? " scale(" + scale + ")" : "");
}
// 因爲重寫了setTransform,所以setPosition也要重新指向方法
function setPosition(el, point) {
  el._leaflet_pos = point;
  if (L.Browser.any3d) {
    setTransform(el, point);
  } else {
    el.style.left = point.x + "px";
    el.style.top = point.y + "px";
  }
}

自定義集成popup模塊

L.CustomPopup = L.Popup.extend({
  _initLayout: function() {
    // 此處生成的容器,Leaflet會根據其類名自動適配Transform,匹配樣式,所以如果要自定義的話,該部分樣式要自己重寫,我這裏只解決了自適應容器的問題,以下采用原生容器,所以後面我會加上樣式覆蓋。
    var prefix = "leaflet-popup",
      container = (this._container = L.DomUtil.create(
        "div",
        prefix + " " + (this.options.className || "") + " leaflet-zoom-animated"
      ));
    var wrapper = container;
    this._contentNode = L.DomUtil.create("div", prefix + "-content", wrapper);
    L.DomEvent.disableClickPropagation(wrapper)
      .disableScrollPropagation(this._contentNode)
      .on(wrapper, "contextmenu", L.DomEvent.stopPropagation);
    this._tipContainer = L.DomUtil.create(
      "div",
      prefix + "-tip-container",
      container
    );
    this._tip = L.DomUtil.create("div", prefix + "-tip", this._tipContainer);
  },
  // 位置更新
  _updatePosition: function() {
    if (!this._map) {
      return;
    }
    var pos = this._map.latLngToLayerPoint(this._latlng),
      offset = L.point(this.options.offset),
      anchor = [0, 0];
    if (this._zoomAnimated) {
      setPosition(this._container, pos.add(anchor));
    } else {
      offset = offset.add(pos).add(anchor);
    }
    var bottom = (this._containerBottom = -offset.y),
      left = (this._containerLeft =
        -Math.round(this._containerWidth / 2) + offset.x);
    // bottom position the popup in case the height of the popup changes (images loading etc)
    this._container.style.bottom = bottom + "px";
    this._container.style.left = left + "px";
  },
  // 重寫層級變化觸發更新
  _animateZoom: function(e) {
    var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
      anchor = [0, 0];
    setPosition(this._container, pos.add(anchor));
  }
});

因爲使用的是原生生成的容器,所以這裏要強制覆蓋原生CSS

.leaflet-popup-content{
   width: fit-content !important;
   margin: 0 !important;
}

以上就完成了自定義彈窗了

然後就是正常使用

let testPopup = new L.CustomPopup({
   className: "layerPopup",
   // minWidth: 300, // 這裏不再傳入固定寬度啦
   offset: L.point(0, -10)
}).setLatLng(latlng).setContent("<div id='popupContainer'></div>");
testPopup.openOn(this.map);

這裏註冊一下用於彈窗的組件

import popupAlarmContainer from "./layerPopup/alarm.vue";
let PopupAlarm = Vue.extend(popupAlarmContainer);
let popupContent = new PopupAlarm({
   propsData: {
      popupInfo: {} // 需要傳入的props參數
   }
});
popupContent.$mount("#popupContainer");

到這裏爲止alarm.vue組件就註冊到這個自定義彈窗了,後續我們只要針對alarm.vue進行彈窗組件的樣式控制和邏輯業務開發就好了,完美解脫Leaflet原生Popup的控制。

後續有時間我會進一步優化,並將該組件擴展上傳至npm庫及Leaflet Plugins

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章