leaflet中绘制轨迹线的大量轨迹点,解决大量 marker 绑定 tooltip 同时显示导致的性能问题

优化思路:获取屏幕可视区范围,判断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;
相关推荐
AI视觉网奇20 分钟前
音频获取长度
java·前端·python
小喷友1 小时前
第 6 章:API 路由(后端能力)
前端·react.js·next.js
zwjapple1 小时前
Next.js 中使用 MongoDB 完整指南
开发语言·javascript·mongodb
像素之间1 小时前
elementui中rules的validator 用法
前端·javascript·elementui
小高0071 小时前
🚀把 async/await 拆成 4 块乐高!面试官当场鼓掌👏
前端·javascript·面试
CF14年老兵1 小时前
SQL 是什么?初学者完全指南
前端·后端·sql
2401_837088501 小时前
AJAX快速入门 - 四个核心步骤
前端·javascript·ajax
一月是个猫1 小时前
前端工程化之Lint工具链
前端
小潘同学1 小时前
less 和 sass的区别
前端
无羡仙1 小时前
当点击链接不再刷新页面
前端·javascript·html