Jinuss's blog Jinuss's blog
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

前端可视化
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 框架

  • core模块

  • dom模块

  • control

  • geometry

  • geo

  • layer

    • marker

      • Marker
      • Marker.Drag
      • Icon
      • Icon.Default
      • DivIcon
    • tile

    • vector

    • Layer
      • 概述
      • 源码分析
        • 源码实现
        • Layer基类实现
        • Layer类详解
        • 核心方法
        • Layer类深入
        • Map类与Layer类交互方法
        • Map类扩展详解
        • 方法介绍
        • 总结
        • Layer基类
        • Map类扩展
    • LayerGroup
    • FeatureGroup
    • DivOverlay
    • Popup
    • Tooltip
    • ImageOverlay
    • SVGOverlay
    • VideoOverlay
    • GeoJSON
  • Map

  • 《Leaflet源码》笔记
  • layer
东流
2025-04-14
目录

Layer

# 概述

Layer类是Leaflet中所有图层的基类,提供了一些通用的方法和属性。所有的图层类都应该继承自Layer类。

# 源码分析

# 源码实现

源码中主要包括Layer基类的实现以及图层Layer类与Map类之间交互混入的方法。

# Layer基类实现

Layer基类实现如下:

export var Layer = Evented.extend({
  // 默认配置项
  options: {
    pane: "overlayPane", // 图层默认挂载
    attribution: null, // 图层默认属性
    bubblingMouseEvents: true, // 鼠标事件是否冒泡
  },
  addTo: function (map) {
    map.addLayer(this);
    return this;
  },
  remove: function () {
    return this.removeFrom(this._map || this._mapToAdd);
  },
  removeFrom: function (obj) {
    if (obj) {
      obj.removeLayer(this);
    }
    return this;
  },
  getPane: function (name) {
    return this._map.getPane(
      name ? this.options[name] || name : this.options.pane
    );
  },
  addInteractiveTarget: function (targetEl) {
    this._map._targets[Util.stamp(targetEl)] = this;
    return this;
  },
  removeInteractiveTarget: function (targetEl) {
    delete this._map._targets[Util.stamp(targetEl)];
    return this;
  },
  getAttribution: function () {
    return this.options.attribution;
  },
  _layerAdd: function (e) {
    var map = e.target;
    if (!map.hasLayer(this)) {
      return;
    }

    this._map = map; // 设置关联的地图实例
    this._zoomAnimated = map._zoomAnimated; // 是否支持缩放动画
    if (this.getEvents) { // 绑定事件
      var events = this.getEvents();
      map.on(events, this);
      this.once(
        "remove",
        function () {
          map.off(events, this); // 移除时解绑事件
        },
        this
      );
    }

    this.onAdd(map);
    this.fire("add"); //触发add事件
    map.fire("layeradd", { layer: this }); //地图触发layeradd事件
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

# Layer类详解

Layer基类继承于Evented类,因此所有图层类支持事件如on、off、fire等。

# 核心方法
  1. addTo(map):将图层添加到指定的地图对象map中,并返回当前图层对象允许链式调用。

  2. remove():从当前地图对象中移除图层。

  3. removeFrom(obj):从指定的对象obj(如Map或LayerGroup)中移除图层。

  4. getPane(name):获取指定名称的图层容器,默认获取overlayPaneDOM容器。

  5. addInteractiveTarget(targetEl)和removeInteractiveTarget:

  • 用途:将DOM元素注册为交互目标,比如Marker的图标元素
  • 实现:使用Util.stamp生成唯一ID,绑定到当前图层实例的_map._targets对象中
  • 作用:
    • 当鼠标事件发生在该DOM元素上时,会触发地图的mousemove、click等事件,并且事件对象的target属性指向该DOM元素。
    • 可以通过map._targets获取所有的交互目标元素及其对应的图层实例。
    • 这对于处理复杂的交互场景非常有用,比如在地图上显示多个Marker,并且希望在点击其中一个Marker时触发相应的事件。
  1. getAttribution():获取图层的属性信息。

  2. _layerAdd(e):图层添加到地图时的回调函数,主要用于处理事件绑定、图层添加到地图的逻辑。其中主要涉及到两个方法getEvents和onAdd,分别用于获取图层相关的事件和图层添加到地图后的逻辑,它们都是在Layer基类的子类中实现。

# Layer类深入
  • 流程

    1. 挂载到地图实例,检查缩放动画状态
    2. 通过getEvents获取子类的事件监听配置(如zoomend、move)
    3. 绑定事件到地图实例,图层移除时自动解绑
    4. 调用子类实现的onAdd方法,执行图层添加到地图的逻辑
    5. 触发add事件和地图实例的layeradd事件
    6. 图层添加到地图后,触发add事件和地图实例的layeradd事件
  • 事件管理

    1. add事件:当图层添加到地图时触发
    2. remove事件:当图层从地图移除时触发
    3. layeradd事件:当图层添加到地图时触发,传递图层实例作为参数
    4. layerremove事件:当图层从地图移除时触发,传递图层实例作为参数
  • 设计模式

    • 模板方法模式:onAdd和getEvents由子类实现,父类Layer定义流程
    • 发布-订阅模式:通过事件系统实现图层与地图间通信

# Map类与Layer类交互方法

Map类混入如下:

Map.include({
  addLayer: function (layer) {
    if (!layer._layerAdd) {
      throw new Error("The provided object is not a Layer.");
    }
    var id = Util.stamp(layer);
    if (this._layers[id]) {
      return this; // 避免重复添加
    }
    this._layers[id] = layer; // 存储图层
    layer._mapToAdd = this; // 标记图层待添加到此地图

    if (layer.beforeAdd) {
      layer.beforeAdd(this); // 钩子:图层添加前的预处理
    }

    this.whenReady(layer._layerAdd, layer); // 地图就绪后触发图层初始化
    return this;
  },
  removeLayer: function (layer) {
    var id = Util.stamp(layer);

    if (!this._layers[id]) {
      return this;
    }

    if (this._loaded) {
      layer.onRemove(this); // 调用图层清理逻辑
    }

    delete this._layers[id]; // 移除图层引用

    if (this._loaded) {
      this.fire("layerremove", { layer: layer }); // 触发地图layerremove事件
      layer.fire("remove"); // 触发图层remove事件
    }

    layer._map = layer._mapToAdd = null; // 清除关联
    return this;
  },
  hasLayer: function (layer) {
    return Util.stamp(layer) in this._layers; // 检查图层是否存在
  },
  eachLayer: function (method, context) {
    for (var i in this._layers) {
      method.call(context, this._layers[i]); // 遍历所有图层执行method方法
    }
    return this;
  },
  _addLayers: function (layers) {
    layers = layers ? (Util.isArray(layers) ? layers : [layers]) : []; // 统一转为数组
    for (var i = 0, len = layers.length; i < len; i++) {
      this.addLayers(layers[i]); // 批量添加图层
    }
  },
  _addZoomLimit: function (layer) {
    if (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
      this._zoomBoundLayers[Util.stamp(layer)] = layer; // 记录有限制的图层
      this._updateZoomLevels(); // 更新地图缩放范围
    }
  },
  _removeZoomLimit: function (layer) {
    var id = Util.stamp(layer);
    if (this._zoomBoundLayers[id]) {
      delete this._zoomBoundLayers[id]; // 移除记录
      this._updateZoomLevels(); // 更新地图范围
    }
  },
  _updateZoomLevels: function () {
    var minZoom = Infinity,
      maxZoom = -Infinity,
      oldZoomSpan = this._getZoomSpan();
    // 遍历所有有限制的图层,计算全局最小/最大缩放
    for (var i in this._zoomBoundLayers) {
      var options = this._zoomBoundLayers[i].options;
      minZoom =
        options.minZoom === undefined
          ? minZoom
          : Math.min(minZoom, options.minZoom);
      maxZoom =
        options.maxZoom == undefined
          ? maxZoom
          : Math.max(maxZoom, options.maxZoom);
    }

    this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
    this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
    // 触发事件通知缩放范围变化
    if (oldZoomSpan !== this._getZoomSpan()) {
      this.fire("zoomlevelschange");
    }

    // 自动调整当前缩放级别到有效范围
    if (
      this.options.maxZoom === undefined &&
      this._layersMaxZoom &&
      this.getZoom() > this._layersMaxZoom
    ) {
      this.setZoom(this._layersMaxZoom);
    }

    if (
      this.options.minZoom == undefined &&
      this._layersMinZoom &&
      this.getZoom() < this._layersMinZoom
    ) {
      this.setZoom(this._layersMinZoom);
    }
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# Map类扩展详解

上述代码主要实现地图对图层的管理,比如图层的添加、移除、遍历管理,以及处理图层缩放级别限制的逻辑

# 方法介绍
  1. addLayer(layer)

    • 作用:将图层添加到地图
    • 关键步骤
      • 检查图层合法性,即参数layer需要存在_layerAdd方法
      • 通过Util.stamp生成唯一ID避免重复
      • 调用beforeAdd钩子(如TileLayer预加载)
      • 地图就绪后调用_layerAdd(触发图层渲染)
  2. removeLayer(layer)

    • 作用:从地图移除图层
    • 关键步骤:
      • 调用onRemove清理DOM或事件
      • 触发layerremove事件和remove事件
      • 清除图层关联
  3. hasLayer(layer)和eachLayer(fn)

    • 用途
      • hasLayer:检查图层是否存在
      • eachLayer:遍历所有图层执行回调函数
  4. _addLayers(layers)

    • 场景:处理单个或多个图层参数,简化调用
  5. _addZoomLimit(layer)和_removeZoomLimit(layer)

    • 用途:
      • _addZoomLimit:记录有限制的图层
      • _removeZoomLimit:移除记录的图层
  6. _updateZoomLevels()

    • 作用:更新地图的缩放级别范围
    • 关键步骤:
      • 遍历所有有限制的图层,计算全局最小/最大缩放
      • 更新_layersMaxZoom和_layersMinZoom
      • 触发zoomlevelschange事件通知缩放范围变化
      • 自动调整当前缩放级别到有效范围

# 总结

# Layer基类

Leaflet中Layer基类为所有图层提供了基础功能:

  1. ​​生命周期管理​​:添加到地图、移除、事件绑定。
  2. DOM 和交互管理​​:通过窗格挂载内容,注册交互目标。
  3. ​版权信息收集​​:统一处理版权显示。
  4. ​事件系统集成​​:支持自定义事件响应。

子类只需专注于具体的渲染逻辑(如 onAdd)和交互实现,复用基础能力。例如,TileLayer 负责加载瓦片,Marker 负责显示图标,均继承自 Layer。

# Map类扩展

Leaflet 的 Map 类提供了核心的图层管理能力:

  1. ​​生命周期管理​​:添加、移除、遍历图层。
  2. 事件驱动​​:通过事件通知图层状态变化。
  3. 缩放限制协调​​:动态调整地图缩放范围,确保所有图层的可见性约束被满足。
  4. 扩展性​​:通过钩子方法(beforeAdd、onRemove)支持自定义图层行为
编辑 (opens new window)
上次更新: 2025/04/24, 05:35:29
Circle
LayerGroup

← Circle LayerGroup→

最近更新
01
GeoJSON
05-08
02
Circle
04-15
03
CircleMarker
04-15
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式