优化思路:获取屏幕可视区范围,判断tooltip 的经纬度是否在可视区范围内,如果在就显示,如果不在就隐藏,达到优化效果
代码:
javascript
/**
* Leaflet Tooltip 视窗优化演示
*
* 这个文件展示了如何在其他 Leaflet 项目中实现类似的 tooltip 优化方案
* 主要解决大量 marker 绑定 tooltip 导致的性能问题
*
* 使用方法:
* 1. 引入此文件中的优化函数
* 2. 替换原有的 bindTooltip 调用
* 3. 在地图事件中调用更新方法
*/
/**
* 防抖
* @param {*} fn 需要防抖的方法
* @param {*} delay 防抖时间
* @param {*} atOnce 是否需要立即执行
* @returns
* how use vue3+ts
* (1) 首次执行 设置时间内只执行首次 适用于新增、设置、修改等
* let addTest = _.myDebounce(function(e: any) {
addNum();
}, 2000,true);
let addNum = () => {
console.log("打印参数");
};
*
*(2)设置时间内只执行最后一次 适用于筛选等
let addTest = _.myDebounce(function(e: any) {
addNum();
}, 2000,false);
let addNum = () => {
console.log("打印参数");
};
*/
const myDebounce = (fn, time, atOnce) => {
let delay = time || 200;
let timer = null;
let count = 0;
return function () {
const _this = this;
const args = arguments;
// 如果是立即执行
if (atOnce) {
// 第一次直接执行不用等
if (count == 0) {
fn.apply(_this, args);
count++;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
count = 0;
// fn.apply(_this, args);
}, delay);
}
} else {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(_this, args);
}, delay);
}
};
};
/**
* Tooltip 视窗优化类
* 管理 Leaflet 地图中的 tooltip 显示优化
*/
export class LeafletTooltipOptimizer {
constructor(map) {
this.map = map;
this.visibleTooltipMarkers = new Set(); // 可视区域内已绑定tooltip的marker
this.allTooltipMarkers = new Map(); // 所有需要tooltip的marker信息
this.updateDebounced = myDebounce(this.updateVisibleTooltips.bind(this), 200, false);
this.initEventListeners();
}
/**
* 初始化地图事件监听
*/
initEventListeners() {
if (this.map) {
this.map.on("moveend", this.updateDebounced);
this.map.on("zoomend", this.updateDebounced);
this.map.on("resize", this.updateDebounced);
console.log("Tooltip优化事件监听器已初始化");
}
}
/**
* 检查marker是否在可视区域内
*/
isMarkerInViewport(marker) {
if (!marker || !this.map) return false;
try {
const bounds = this.map.getBounds();
const markerLatLng = marker.getLatLng();
return bounds.contains(markerLatLng);
} catch (error) {
console.warn("检查marker可视性时出错:", error);
return false;
}
}
/**
* 为marker绑定tooltip(延迟绑定)
* @param {L.Marker} marker
* @param {string} content tooltip内容
* @param {Object} options tooltip选项
*/
bindTooltipIfVisible(marker, content, options = {}) {
if (!marker) return;
// 存储tooltip配置信息
this.allTooltipMarkers.set(marker, { content, options });
// 如果在可视区域内且未绑定tooltip,则绑定
if (this.isMarkerInViewport(marker) && !this.visibleTooltipMarkers.has(marker)) {
marker.bindTooltip(content, options);
this.visibleTooltipMarkers.add(marker);
}
}
/**
* 更新可视区域内的tooltip绑定
*/
updateVisibleTooltips() {
if (!this.map || this.allTooltipMarkers.size === 0) return;
try {
this.allTooltipMarkers.forEach((tooltipConfig, marker) => {
const isVisible = this.isMarkerInViewport(marker);
const hasTooltip = this.visibleTooltipMarkers.has(marker);
if (isVisible && !hasTooltip) {
// 进入可视区域,绑定tooltip
marker.bindTooltip(tooltipConfig.content, tooltipConfig.options);
this.visibleTooltipMarkers.add(marker);
} else if (!isVisible && hasTooltip) {
// 离开可视区域,解绑tooltip
marker.unbindTooltip();
this.visibleTooltipMarkers.delete(marker);
}
});
} catch (error) {
console.error("更新可视tooltip时出错:", error);
}
}
/**
* 清理marker的tooltip记录
* @param {L.Marker} marker
*/
cleanupMarkerTooltip(marker) {
if (marker) {
this.allTooltipMarkers.delete(marker);
this.visibleTooltipMarkers.delete(marker);
}
}
/**
* 清理所有tooltip记录
*/
clearAllTooltips() {
this.allTooltipMarkers.clear();
this.visibleTooltipMarkers.clear();
}
/**
* 清理事件监听器
*/
destroy() {
if (this.map) {
this.map.off("moveend", this.updateDebounced);
this.map.off("zoomend", this.updateDebounced);
this.map.off("resize", this.updateDebounced);
}
this.clearAllTooltips();
}
/**
* 获取统计信息
*/
getStats() {
return {
totalMarkers: this.allTooltipMarkers.size,
visibleMarkers: this.visibleTooltipMarkers.size,
hiddenMarkers: this.allTooltipMarkers.size - this.visibleTooltipMarkers.size,
};
}
}
/**
* 使用示例:
*
* // 1. 创建优化器实例
* const tooltipOptimizer = new LeafletTooltipOptimizer(map);
*
* // 2. 使用优化后的tooltip绑定方法
* markers.forEach(marker => {
* const tooltipContent = `<div>纬度: ${marker.getLatLng().lat}</div><div>经度: ${marker.getLatLng().lng}</div>`;
* const tooltipOptions = {
* permanent: false,
* direction: "top",
* offset: [0, -10]
* };
*
* // 替换原来的 marker.bindTooltip(content, options)
* tooltipOptimizer.bindTooltipIfVisible(marker, tooltipContent, tooltipOptions);
* });
*
* // 3. 在需要清理marker时调用
* map.eachLayer(layer => {
* if (layer instanceof L.Marker) {
* tooltipOptimizer.cleanupMarkerTooltip(layer);
* map.removeLayer(layer);
* }
* });
*
* // 4. 组件销毁时清理
* tooltipOptimizer.destroy();
*/
export default LeafletTooltipOptimizer;