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

    • Control
    • Control.Scale
      • 概述
      • 源码分析
        • 源码实现
        • 源码详细分析
        • 类定义与选项
        • 控件生命周期方法
        • 核心逻辑方法
        • 单位更新逻辑
        • 辅助方法
        • 工厂函数
      • 总结
    • Control.Zoom
    • Control.Attribution
    • Control.Layers
  • geometry

  • geo

  • layer

  • Map

  • 《Leaflet源码》笔记
  • control
东流
2025-03-24
目录

Control.Scale

# 概述

Control.Scale 是一个用于显示地图比例尺的控件,是 Leaflet 中实现比例尺控件的核心逻辑,用于在地图上动态显示公制(米/千米)和英制(英尺/英里)的比例尺。

# 源码分析

# 源码实现

Control.Scale的源码实现如下:

export var Scale = Control.extend({
  options: {
    position: "bottomleft", // 控件的位置
    maxWidth: 100, // 控件的最大宽度 (像素)
    metric: true, // 是否显示公制比例尺 米/千米
    imperial: true, // 是否显示英制比例尺 英尺/英里
    updateWhenIdle: false, // 是否在地图空闲时更新比例尺
  },
  onAdd: function (map) {
    var className = "leaflet-control-scale",
      container = DomUtil.create("div", className), //创建容器
      options = this.options;

    // 添加公制/英制比例尺元素
    this._addScales(options, className + "-line", container);

    // 监听地图移动事件,动态更新比例尺
    map.on(options.updateWhenIdle ? "moveend" : "move", this._update, this);
    map.whenReady(this._update, this); // 初始化时更新

    return container;
  },
  onRemove: function (map) {
    // 移除监听事件
    map.off(
      this.options.updateWhenIdle ? "moveend" : "move",
      this._update,
      this
    );
  },

  _addScales: function (options, className, container) {
    if (options.metric) {
      this._mScale = DomUtil.create("div", className, container);
    }
    if (options.imperial) {
      this._iScale = DomUtil.create("div", className, container);
    }
  },
  _update: function () {
    var map = this._map,
      y = map.getSize().y / 2;

    // 计算地图上maxWidth像素对应的实际距离(单位:米)
    var maxMeters = map.distance(
      map.containerPointToLatLng([0, y]),
      map.containerPointToLatLng([this.options.maxWidth, y])
    );

    this._updateScales(maxMeters);
  },

  _updateScales: function (maxMeters) {
    if (this.options.metric && maxMeters) {
      this._updateMetric(maxMeters);
    }
    if (this.options.imperial && maxMeters) {
      this._updateImperial(maxMeters);
    }
  },

  _updateMetric: function (maxMeters) {
    var meters = this._getRoundNum(maxMeters),
      label = meters < 1000 ? meters + " m" : meters / 1000 + " km";

    this._updateScale(this._mScale, label, meters / maxMeters);
  },

  _updateImperial: function (maxMeters) {
    var maxFeet = maxMeters * 3.2808399,
      maxMiles,
      miles,
      feet;

    if (maxFeet > 5280) {
      maxMiles = maxFeet / 5280;
      miles = this._getRoundNum(maxMiles);
      this._updateScale(this._iScale, miles + " mi", miles / maxMiles);
    } else {
      feet = this._getRoundNum(maxFeet);
      this._updateScale(this._iScale, feet + " ft", feet / maxFeet);
    }
  },

  _updateScale: function (scale, text, ratio) {
    scale.style.width = Math.round(this.options.maxWidth * ratio) + "px";
    scale.innerHTML = text;
  },

  _getRoundNum: function (num) {
    var pow10 = Math.pow(10, (Math.floor(num) + "").length - 1),
      d = num / pow10;

    d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1;

    return pow10 * d;
  },
});

export var scale = function (options) {
  return new Scale(options);
};
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

# 源码详细分析

# 类定义与选项

  • 继承自Control: 继承自Control基类类,用于创建可添加到地图上的控件。

  • 核心配置项:通过metric和imperial控制比例尺单位显示

# 控件生命周期方法

  • onAdd:控件添加到地图时触发

onAdd方法主要用于创建比例尺控件的 DOM 元素,并将其添加到地图容器中。

  • onRemove:控件从地图移除时触发

onRemove方法就是清理事件监听,可以防止内存泄漏。

# 核心逻辑方法

  • _addScales:创建公制/英制比例尺 DOM 元素

_addScales方法根据配置项创建公制和英制比例尺的 DOM 元素。

  • _update:更新比例尺的核心方法

_update方法的核心逻辑就是通过map.distance方法计算当前视图下maxWidth像素对应的实际距离(基于地图投影)。

  • _updateScales:根据实际距离更新比例尺

_updateScales方法根据实际距离计算公制和英制比例尺的显示内容,并调用_updateScale方法更新比例尺的 DOM 元素。

  • _updateMetric:更新公制比例尺

    _updateMetric方法根据实际距离计算公制比例尺的显示内容,并调用_updateScale方法更新比例尺的 DOM 元素。

  • _updateImperial:更新英制比例尺

    _updateImperial方法根据实际距离计算英制比例尺的显示内容,并调用_updateScale方法更新比例尺的 DOM 元素。

# 单位更新逻辑

  1. 公制单位更新_updateMetric
  • 逻辑:若 maxMeters < 1000 显示米(m),否则显示千米(km)。
  • 示例:若 maxMeters = 500,则显示 500 m;若 maxMeters = 1500,则显示 1.5 km。
  1. 英制单位更新_updateImperial
  • 逻辑:若 maxMeters > 5280,则显示英里(mi),否则显示英尺(ft)。
  • ​换算公式:1 米 ≈ 3.28084 英尺,1 英里 = 5280 英尺
# 辅助方法
  1. _updateScale:更新比例尺的 DOM 元素
  • 功能:根据比例尺的比例更新比例尺的宽度和显示内容。
  • 参数:
    • scale:比例尺的 DOM 元素。
    • text:比例尺的显示内容。
    • ratio:比例尺的比例,用于计算比例尺的宽度。
  1. _getRoundNum:获取合适的显示数字
  • 功能:根据比例尺的实际距离,返回合适的显示数字。
  • 参数:
    • num:比例尺的实际距离。

# 工厂函数

简化控件的创建,例如 L.control.scale({ metric: true })。

# 总结

  1. 动态更新:比例尺会随地图缩放/移动自动重新计算。
  2. 跨单位支持:同时处理公制和英制单位,且自动选择合适单位(如米 → 千米,英尺 → 英里)。
  3. 数值优化:通过 _getRoundNum 避免显示复杂小数(如 1.333 km)。
  4. 性能优化:通过 updateWhenIdle 避免频繁更新
编辑 (opens new window)
上次更新: 2025/04/03, 07:19:02
Control
Control.Zoom

← Control Control.Zoom→

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