概述
Marker
类用于创建一个标记点对象,可以用于在地图上添加标记点。Marker
类继承自Layer
类,提供了一些方法用于创建标记点对象。
源码分析
源码实现
Marker
类实现如下:
js
export var Marker = Layer.extend({
options: {
icon: new IconDefault(), // 默认图标实例
interactive: true, // 是否可交互(点击/悬停)
keyboard: true, // 是否支持键盘导航
title: "", // 图标的title属性
alt: "Marker", // 图标的alt属性
zIndexOffset: 0, // 控制图层堆叠顺序
opacity: 1, // 图标透明度
riseOnHover: false, // 是否在悬停时提升图层
riseOffset: 250, // 上浮的z-index偏移量
pane: "markerPane", // 图标挂载的DOM窗格
shadowPane: "shadowPane", // 阴影挂载的DOM窗格
bubblingMouseEvents: false, // 阻止鼠标事件冒泡
autoPanOnFocus: true, // 聚焦时自动平移地图
draggable: false, // 是否可拖动图标
autoPan: false, // 拖动时是否自动平移地图
autoPanPadding: [50, 50],// 自动平移的填充距离
autoPanSpeed: 10, // 自动平移的速度
},
initialize: function (latlng, options) {
Util.setOptions(this, options);
this._latlng = latLng(latlng); // 标准化经纬度
},
onAdd: function (map) {
this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
if (this._zoomAnimated) {
map.on("zoomanim", this._animateZoom, this); // 监听缩放动画
}
this._initIcon(); // 创建图标和阴影
this.update(); // 更新图标位置
},
// 清理资源
onRemove: function (map) {
if (this.dragging && this.dragging.enabled()) {
this.dragging.draggable = true;
this.dragging.removeHooks(); // 清理图标拖动事件
}
delete this.dragging;
if (this._zoomAnimated) {
map.off("zoomanim", this._animateZoom, this); // 取消事件监听
}
this._removeIcon(); // 移除图标 DOM
this._removeShadow(); // 移除阴影 DOM
},
getEvents: function () {
// 定义事件
return {
zoom: this.update,
viewreset: this.update,
};
},
getLatLng: function () {
return this._latlng; // 获取图标经纬度
},
setLatLng: function (latlng) {
var oldLatLng = this._latlng;
this._latlng = latLng(latlng); // 更新坐标
this.update(); // 刷新位置
return this.fire("move", { oldLatLng: oldLatLng, latLng: this._latlng }); // 触发移动事件
},
setZIndexOffset: function (offset) {
this.options.zIndexOffset = offset; //更新zIndexOffset属性
return this.update(); // 更新图标位置
},
getIcon: function () {
return this.options.icon; // 获取图标实例
},
setIcon: function (icon) { // 设置图标实例
if (this.options.icon) { // 移除旧的图标
this._removeIcon();
}
this.options.icon = icon; // 更新图标
if (this._map) {
this._initIcon(); // 初始化图标
this.update(); // 更新位置
}
if (this._popup) {
this.bindPopup(this._popup, this._popup.options);
}
return this;
},
getElement: function () {
return this._icon; // 获取图标的DOM元素
},
update: function () {
if (this._icon && this._map) {
var pos = this._map.latLngToLayerPoint(this._latlng).round(); // 将经纬度转换为屏幕坐标
this._setPos(pos); // 设置图标的位置
}
},
_initIcon: function () { // 初始化图标,创建图标以及阴影图标
var options = this.options,
classToAdd = "leaflet-zoom-" + (this._zoomAnimated ? "animated" : "hide");
var icon = options.icon.createIcon(this._icon),
addIcon = false;
if (icon !== this._icon) {
if (this._icon) {
this._remove();
}
addIcon = true;
if (options.title) {
icon.title = options.title;
}
if (icon.tagName == "IMG") {
icon.alt = options.alt || "";
}
}
DomUtil.addClass(icon, classToAdd);
if (options.keyboard) {
icon.tabIndex = "0";
icon.setAttribute("role", "button");
}
this._icon = icon;
if (options.riseOnHover) {
this.on({
mouseover: this._bringToFront,
mouseout: this._resetZIndex,
});
}
if (this.options.autoPanOnFocus) {
DomEvent.on(icon, "focus", this._panOnFocus, this);
}
var newShadow = options.icon.createShadow(this._shadow),
addShadow = false;
if (newShadow !== this._shadow) {
this._removeShadow();
addShadow = true;
}
if (newShadow) {
DomUtil.addClass(newShadow, classToAdd);
newShadow.alt = "";
}
this._shadow = newShadow;
if (options.opacity < 1) {
this._updateOpacity();
}
if (addIcon) {
this.getPane().appendChild(this._icon);
}
this._initInteraction();
if (newShadow && addShadow) {
this.getPane(options.shadowPane).appendChild(this._shadow);
}
},
_removeIcon: function () {
// 移除图标
if (this.options.riseOnHover) { //若果 riseOnHover 为 true,则移除鼠标悬停事件
this.off({
mouseover: this._bringToFront,
mouseout: this._resetZIndex,
});
}
if (this.options.autoPanOnFocus) { // 若果 autoPanOnFocus 为 true,则移除焦点事件
DomEvent.off(this._icon, "focus", this._panOnFocus, this);
}
DomUtil.remove(this._icon); // 移除图标 DOM
this.removeInteractiveTarget(this._icon); // 移除交互目标
this._icon = null; // 清空图标
},
_removeShadow: function () { // 移除阴影图标
if (this._shadow) {
DomUtil.remove(this._shadow);
}
this._shadow = null;
},
_setPos: function (pos) {
// 内部方法:更新图标位置,通过DomUtil.setPosition更新位置
if (this._icon) {
DomUtil.setPosition(this._icon, pos);
}
if (this._shadow) {
DomUtil.setPosition(this._shadow, pos);
}
this._zIndex = pos.y + this.options.zIndexOffset;
this._resetZIndex();
},
_updateZIndex: function (offset) {
// 内部方法:更新图标层级
if (this._icon) {
this._icon.style.zIndex = this._zIndex + offset;
}
},
_animateZoom: function () {
// 内部方法:处理缩放动画
var pos = this._map
._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center)
.round();
// 在缩放动画中平滑更新位置
this._setPos(pos);
},
_initInteraction: function () {
// 初始化图标交互行为,判断图标是否支持拖动交互
if (!this.options.interactive) {
return;
}
DomUtil.addClass(this._icon, "leaflet-interactive");
if (MarkerDrag) {
var draggable = this.options.draggable;
if (this.dragging) {
draggable = this.dragging.enabled();
this.dragging.disable();
}
this.dragging = new MarkerDrag(this);
if (draggable) {
this.dragging.enable();
}
}
},
setOpacity: function (opacity) {
// 修改图标透明度
this.options.opacity = opacity;
if (this._map) {
this._updateOpacity();
}
return this;
},
_updateOpacity: function () {
// 内部方法:修改图标透明度
var opacity = this.options.opacity;
if (this._icon) {
DomUtil.setOpacity(this._icon, opacity);
}
if (this._shadow) {
DomUtil.setOpacity(this._shadow, opacity);
}
},
_bringToFront: function () {
this._updateZIndex(this.options.riseOffset); // 上浮图标层级
},
_resetZIndex: function () {
this._updateZIndex(0); // 重置图标层级
},
_panOnFocus: function () {
// 自动平移
var map = this._map;
if (!map) {
return;
}
var iconOpts = this.options.icon.options;
var size = iconOpts.iconSize ? point(iconOpts.iconSize) : point(0, 0);
var anchor = iconOpts.iconAnchor ? point(iconOpts.iconAnchor) : point(0, 0);
map.panInside(this._latlng, {
paddingTopLeft: anchor,
paddingBottomRight: size.subtract(anchor),
}); // 平移地图确保图标可见
},
_getPopupAnchor: function () {
return this.options.icon.options.popupAnchor;
},
_getTooltipAnchor: function () {
return this.options.icon.options.tooltipAnchor;
},
});
// 工厂函数,接受经纬度以及其它选项
export function marker(latlng, options) {
return new Marker(latlng, options);
}
源码详解
Marker
类继承自Layer
类,复用图层管理、事件等基础功能。
核心方法
initialize
:初始化方法,接受latlng
(坐标)和options
,设置默认选项和初始化_latlng
属性。onAdd
:添加到地图时的回调方法,初始化图标和阴影,设置事件监听。onRemove
:从地图移除时的回调方法,清理资源。getEvents
:返回事件映射,用于绑定事件。getLatLng
:获取标记点的经纬度。setLatLng
:设置标记点的经纬度,并触发移动事件。setZIndexOffset
:设置标记点的zIndex偏移量。getIcon
:获取标记点的图标实例。setIcon
:设置标记点的图标实例。getElement
:获取标记点的DOM元素。update
:更新标记点的位置。_initIcon
:初始化图标,创建图标和阴影。_removeIcon
:移除图标。_removeShadow
:移除阴影。_setPos
:设置标记点的位置。_updateZIndex
:更新标记点的zIndex。_animateZoom
:缩放动画回调方法。_initInteraction
:初始化交互功能。setOpacity
:设置标记点的透明度。_updateOpacity
:更新标记点的透明度。_bringToFront
:将标记点提升到最前面。_resetZIndex
:重置标记点的zIndex。_panOnFocus
:焦点事件回调方法,自动平移地图。_getPopupAnchor
:获取弹出框的锚点。_getTooltipAnchor
:获取提示框的锚点。
总结
- 核心功能:将经纬度坐标转换为屏幕位置,渲染图标,支持交互(点击、拖动)。
- 扩展性 :通过
Icon
类自定义图标,MarkerDrag
实现拖动。 - 性能优化:缩放动画平滑过渡,按需更新 DOM。
- 无障碍 :支持键盘导航、
alt/title
属性