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;
相关推荐
J***Q29219 小时前
Vue数据可视化
前端·vue.js·信息可视化
JIngJaneIL19 小时前
社区互助|社区交易|基于springboot+vue的社区互助交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·社区互助
ttod_qzstudio20 小时前
深入理解 Vue 3 的 h 函数:构建动态 UI 的利器
前端·vue.js
芳草萋萋鹦鹉洲哦20 小时前
【elemen/js】阻塞UI线程导致的开关卡顿如何优化
开发语言·javascript·ui
_大龄20 小时前
前端解析excel
前端·excel
1***s63221 小时前
Vue图像处理开发
javascript·vue.js·ecmascript
一 乐21 小时前
应急知识学习|基于springboot+vue的应急知识学习系统(源码+数据库+文档)
数据库·vue.js·spring boot
槁***耿21 小时前
JavaScript在Node.js中的事件发射器
开发语言·javascript·node.js
一叶茶21 小时前
移动端平板打开的三种模式。
前端·javascript
前端大卫21 小时前
一文搞懂 Webpack 分包:async、initial 与 all 的区别【附源码】
前端