源码分析之Leaflet中Marker.Drag

概述

Marker.Drag 是 Leaflet 中用于实现标记拖动功能的插件。它允许用户通过鼠标或触摸设备拖动标记到地图上的新位置。

源码分析

源码实现

MarkerDrag类实现如下:

export var MarkerDrag = Handler.extend({
  initialize: function (marker) {
    this._marker = marker;
  },
  addHooks: function () {
    var icon = this._marker._icon;
    if (!this._draggable) {
      this._draggable = new Draggable(icon, icon, true); // 创建可拖拽对象
    }

    this._draggable
      .on(
        {
          dragstart: this._onDragStart,
          predrag: this._onPreDrag,
          drag: this._onDrag,
          dragend: this._onDragEnd,
        },
        this
      )
      .enable(); // 绑定事件并启用

    DomUtil.addClass(icon, "leaflet-marker-draggable"); // 添加拖拽样式
  },
  removeHooks: function () {
    this._draggable
      .off(
        {
          dragstart: this._onDragStart,
          predrag: this._onPreDrag,
          drag: this._onDrag,
          dragend: this._onDragEnd,
        },
        this
      )
      .disable(); // 解绑事件并禁用

    if (this._marker._icon) {
      DomUtil.removeClass(this._marker._icon, "leaflet-marker-draggable"); // 移除拖拽样式
    }
  },
  moved: function () {
    return this._draggable && this._draggable._moved; // 判断是否发生移动
  },
  _adjustPan: function (e) {
    var marker = this._marker,
      map = marker._map,
      speed = this._marker.options.autoPanSpeed,
      padding = this._marker.options.autoPanPadding,
      iconPos = DomUtil.getPosition(marker._icon),
      bounds = map.getPixelBounds(),
      origin = map.getPixelOrigin();

    var panBounds = toBounds(
      bounds.min._subtract(origin).add(padding),
      bounds.max._subtract(origin).subtract(padding)
    );
    
    // 计算 Marker位置是否超出地图边界
    if (!panBounds.contains(iconPos)) {
      // Compute incremental movement
      // 计算平移量并移动地图
      var movement = toPoint(
        (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) /
          (bounds.max.x - panBounds.max.x) -
          (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) /
            (bounds.min.x - panBounds.min.x),

        (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) /
          (bounds.max.y - panBounds.max.y) -
          (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) /
            (bounds.min.y - panBounds.min.y)
      ).multiplyBy(speed);

      map.panBy(movement, { animate: false });

      this._draggable._newPos._add(movement);
      this._draggable._startPos._add(movement);
      // 更新拖拽位置
      DomUtil.setPosition(marker._icon, this._draggable._newPos);
      this._onDrag(e);
      // 递归调用,实现持续平移效果   
      this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
    }
  },
  _onDragStart: function () {
    this._oldLatLng = this._marker.getLatLng(); // 记录原始位置
    this._marker.closePopup && this._marker.closePopup(); // 关闭弹框

    this._marker.fire("movestart").fire("dragstart"); // 触发事件
  },
  _onPreDrag: function (e) {
    if (this._marker.options.autoPan) {
      cancelAnimFrame(this._panRequest);
      this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
    }
  },
  _onDrag: function (e) {
    var marker = this._marker,
      shadow = marker._shadow,
      iconPos = DomUtil.getPosition(marker._icon),
      latlng = marker._map.layerPointToLatLng(iconPos); // 计算新经纬度

    if (shadow) {
      DomUtil.setPosition(shadow, iconPos);
    }

    marker._latlng = latlng; // 更新标记位置
    e.latlng - latlng;
    e.oldLatLng = this._oldLatLng;

    marker.fire("move", e).fire("drag", e); // 触发事件
  },
  _onDragEnd: function (e) {
    cancelAnimFrame(this._panRequest); // 停止自动平移
    delete this._oldLatLng;
    this._marker.fire("moveend").fire("dragend", e); // 触发事件
  },
});

源码详解

  1. 类定义与初始化

    • 作用:定义一个MarkerDrag的处理器,继承Handler类。
    • 关键点
      • initialize方法:初始化MarkerDrag实例,接收一个marker参数。
      • _marker属性:存储传入的marker实例。
  2. 钩子函数(Hooks)

    • 作用:通过addHooksremoveHooks管理拖拽功能的激活与销毁
    • 关键点
      • Draggable对象:基于Marker的图标(_icon)创建,实际控制拖拽行为
      • 事件绑定:监听dragstartpredragdragdragend四个关键事件,对应不同阶段的拖拽逻辑。
      • CSS类:添加leaflet-marker-draggable类,可能用于自定义拖拽时的样式,如光标形状
  3. 自动平移(AutoPan)

    • 作用:当拖拽Marker到地图边缘时,自动平移地图以保持Marker可见
    • 关键点
      • 边界计算:根据autoPanPadding计算安全区域(panBounds)
      • 平滑移动:通过requestAnimFrame实现平滑的逐帧平移,避免卡顿
      • 位置补偿:更新Draggable对象的_newPos_startPos,防止因地图平移导致的Marker位置跳变
  4. 拖拽事件处理

    1. _onDragStart
      • 作用:记录原始位置并关闭相关弹框
      • 关键点
        • this._oldLatLng:存储原始位置,用于计算移动距离
        • marker.closePopup:关闭与Marker相关的弹出框
        • 触发movestartdragstart事件
    2. _onPreDrag
      • 作用:在拖拽开始前检查是否需要自动平移
      • 关键点
        • this._marker.options.autoPan:检查是否启用自动平移
        • this._adjustPan:调用_adjustPan方法进行平移
    3. _onDrag
      • 作用:实时更新Marker位置并触发相关事件
      • 关键点
        • 计算新的经纬度
        • 更新Marker的位置
        • 触发movedrag事件
    4. _onDragEnd
      • 作用:清理状态,结束拖拽操作并触发相关事件
      • 关键点
        • 停止自动平移
        • 触发moveenddragend事件
  5. 辅助方法
    move方法提供外部接口,判断拖拽过程中Marker是否实际移动

核心逻辑

​​

  1. 拖拽初始化​​:通过 Draggable 对象绑定 Marker 图标的拖拽能力。
  2. 自动平移​​:在 predrag 阶段实时检测边界,动态调整地图视口。
  3. 位置同步​​:将拖拽后的像素坐标转换为经纬度,更新 Marker 位置。
  4. ​事件传递​​:在拖拽各阶段触发事件(如 dragstartdrag),允许外部监听并自定义行为。

扩展思考

  • 性能优化​​:使用 requestAnimFrame 确保平移流畅,避免过度渲染。
  • 自定义行为​​:通过 autoPanSpeedautoPanPadding 参数可调整平移灵敏度。
  • ​可扩展性​​:通过继承 Handler,Leaflet 允许开发者自定义其他交互行为(如旋转、缩放)

总结

MarkerDrag类通过Draggable对象实现了Marker的拖拽功能,支持自动平移和自定义样式。通过事件驱动,MarkerDrag提供了丰富的拖拽事件,方便开发者进行自定义操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jinuss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值