首先感谢 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