Mapboxgl popup 自定义弹窗:动态展示数据,动态更新,样式调整
自定义弹窗的开发
自定义弹窗类CustomPopup.js
export default class CustomPopup {
// options.coordinates用于地图定位,必须有,其他属性根据具体业务调整
constructor(options) {
this.popup = null; // mapboxgl.Popup实例
this.options = options; // 配置选项
this.defaultOptions = { // mapboxgl.Popup原生选项
className: "custom-popup" + (options.type ? " popup-" + options.type : ""), // 默认class是custom-popup,如果需要根据type定制,则用到后面的class popup-xxx
// closeOnClick: false, // 是否在点击地图地他地方时关闭弹窗
maxWidth: "296px", // 限制宽度
};
this.newPopup(this.defaultOptions, options);
}
getPopup() {
return this.popup;
}
getId() {
return this.options && this.options.id;
}
getOptions() {
return this.options;
}
remove() {
this.popup && this.popup.remove();
this.popup = null;
}
newPopup(defaultOptions, options) {
let popupHtml = this.createHtml(options); // 根据具体业务需求拼接你需要的html
this.popup = new mapboxgl.Popup(defaultOptions).setLngLat(options.coordinates).setHTML(popupHtml).addTo(window.map); // window.map mapboxgl全局map对象
}
// 更新弹窗内容和弹窗坐标
updatePopup(options) {
this.updatePopupContent(options);
this.updateCoordinates(options.coordinates);
}
// 更新弹窗内容
updatePopupContent(options) {
this.options = options;
let updatePopupHtml = this.createHtml(options.type, options.columns, options.properties, options.id, options.followStatus);
this.popup.setHTML(updatePopupHtml);
}
// 更新弹窗坐标
updateCoordinates(coordinates) {
this.options.coordinates = coordinates;
this.popup.setLngLat(coordinates);
}
// 拼接html,此处省略了具体业务
createHtml() {
let html = `<div class="popup-container">`;
// 标题
let headerHtml = this.createHeader();
html += headerHtml;
// 主要内容(分段)
for (let key in columns) {
if (key !== "标题") {
html += this.createParagraph();
}
}
// 页脚按钮
let btnsHtml = this.createFooter();
html += btnsHtml;
html += `</div>`;
return html;
}
createHeader() {
let html = `<div class="popup-header">`;
html += `<span>` + `你的标题` + `</span>`;
html += `</div>`;
return html;
}
createParagraph() {
let html = `<div class="popup-pa">`;
html += `<div class="popup-pa-title">` + `你的子标题` + `</div>`;
html += `<div class="popup-pa-content">`;
for (let key in subCol) {
html +=
`<div class="popup-pa-item"><span class="popup-pa-item-label">` +
`你的label` +
`: </span><span class="popup-pa-item-value">` +
`你的value` +
`</span></div>`;
}
html += `</div></div>`;
return html;
}
createFooter() {
let html = `<div class="popup-footer">`;
btns.forEach((btn) => {
// 写一个 window.clickEvt(a, b, c) 即可接收点击事件
html += `<button class="popup-btn" onclick="clickEvt('` + a + `','` + b + `','` + c + `')">` + btn + `</button>`;
});
html += `</div>`;
return html;
}
}
自定义弹窗样式
// 自定义弹窗
:deep(.custom-popup) {
// 从点击位置引出的小三角
.mapboxgl-popup-tip {
border-top-color: @theme-background;
border-bottom-color: @theme-background;
}
// 关闭按钮
.mapboxgl-popup-close-button {
height: 32px;
color: @theme-text;
font-size: 2em;
padding: 0 5px;
margin-right: 9px;
}
// 弹窗容器
.mapboxgl-popup-content {
width: 296px;
padding: 0;
background: @theme-background;
color: @theme-text;
// 自定义弹窗内容
.popup-container {
font-size: 14px;
// 标题行
.popup-header {
padding: 0 14px;
display: flex;
height: 32px;
line-height: 32px;
text-align: left;
border-bottom: 1px solid @theme-border-1;
padding-right: 60px;
span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.popup-icon {
height: 32px;
width: 32px;
padding-right: 10px;
}
}
// 段落,可以多段
.popup-pa {
padding: 0 14px;
.popup-pa-title {
padding-top: 2px;
height: 32px;
line-height: 32px;
text-align: left;
color: @theme-text-title;
border-bottom: 1px solid @theme-border-2;
}
.popup-pa-content {
display: flex;
flex-flow: row wrap;
width: 100%;
.popup-pa-item {
width: 49%;
height: 32px;
line-height: 32px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.popup-pa-item-label {
color: @theme-text-label;
}
.popup-pa-item-value {
color: @theme-text-value;
}
}
}
}
// 弹窗脚,主要放按钮
.popup-footer {
padding: 14px;
height: 32px;
line-height: 32px;
text-align: right;
.popup-btn {
padding: 0;
margin-left: 8px;
width: 40px;
height: 22px;
line-height: 22px;
border: 1px solid @theme-border-3;
border-radius: 2px;
font-size: 12px;
color: @theme-text;
background-color: @theme-input-background;
cursor: pointer;
}
}
}
}
}
固定弹窗在窗口显示的位置
默认弹窗是跟随地图移动的,但是如果有固定弹窗位置的需求(这个需求如果一开始提出的话建议你直接不要用popup实现,这是我开发完以后经理才提出的),可以将样式小做以下修改:
:deep(.custom-popup) {
transform: unset !important;
left: 280px;
top: 20px;
// 从点击位置引出的小三角
.mapboxgl-popup-tip {
display: none;
}
}
自定义弹窗的使用和管理
添加和删除
const popup = new CustomPopup({
coordinates: [120, 30],
... // 其他配置项
});
popup.remove();
缓存
可以借助pinia等工具缓存CustomPopup对象
// mapStore
const popupInstance = ref(null); // 该实例为封装过的CustomPopup
const memoPopup = (popup) => {
popupInstance.value = popup;
};
const removePopup = () => {
popupInstance.value && popupInstance.value.remove();
popupInstance.value = null;
};
// 使用
const popup = ...
mapStore.memoPopup(popup);
更新
以使用pinia管理时更新为例
if (mapStore.popupInstance) {
let popupInstance = mapStore.popupInstance;
if (popupInstance.getId() === layerId + ":" + props.id) { // 判断你要更新的数据此时是否正在展示弹窗,我这里用的是图层id+数据id
let op = popupInstance.getOptions();
op.coordinates = coordinates; // 换成新的坐标
op.properties = Object.assign(op.properties, props); // props是新的数据,用Object.assign前后对象只换掉有更新的数据
popupInstance.updatePopup(op);
}
}