leaflet中绘制轨迹线的大量轨迹点,解决大量 marker 绑定 tooltip 同时显示导致的性能问题
优化思路:获取屏幕可视区范围,判断tooltip 的经纬度是否在可视区范围内,如果在就显示,如果不在就隐藏,达到优化效果
代码:
/*** 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的markerthis.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) {// 进入可视区域,绑定tooltipmarker.bindTooltip(tooltipConfig.content, tooltipConfig.options);this.visibleTooltipMarkers.add(marker);} else if (!isVisible && hasTooltip) {// 离开可视区域,解绑tooltipmarker.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;