Leaflet扩展CircleMarker的自定义标记如何实现分组、高亮及弹窗绑定?
Leaflet自定义标记交互与弹窗异常修复方案
问题表现
在GeoJSON多边形上渲染CustomPoint样式的自定义CircleMarker时,出现两个异常:
- 所有标记弹窗内容完全一致
- 鼠标悬停时触发组内全部标记高亮,而非仅当前交互标记


问题原因
- 事件与弹窗绑定到了整个
L.featureGroup容器上,而非每个单独的CustomPoint实例,触发事件时会作用于全组元素,且弹窗内容会被最后一次遍历的feature属性覆盖 - 生成的CustomPoint实例未关联对应GeoJSON的feature属性,高亮逻辑调用时无法读取对应要素数据
onEachFeature遍历逻辑中重复对featureGroup绑定事件,导致事件多次触发
修复方案
- 创建CustomPoint实例时手动关联对应feature属性
- 将鼠标交互事件、弹窗绑定到每个单独的CustomPoint实例上,移除对容器组的全局绑定
- 保留featureGroup仅作为标记容器使用,不承担交互绑定逻辑
修正后完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <style> html, body { height: 100%; margin: 0; } #map { width: 100%; height: 100%; } </style> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.3/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet@1.3.3/dist/leaflet.js"></script> <!--Load plugin --> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-ajax/2.1.0/leaflet.ajax.min.js"></script> </head> <body> <div id="map"></div> <script> L.Canvas.include({ _updateCustomPoint: function (layer) { if (!this._drawing || layer._empty()) { return; } var p = layer._point, ctx = this._ctx, r = Math.max(Math.round(layer._radius), 1); this._drawnLayers[layer._leaflet_id] = layer; ctx.beginPath() ctx.moveTo(p.x, p.y) ctx.lineTo(p.x - 0.58 * r, p.y - r) ctx.arc(p.x, p.y - 2 * r, r, -Math.PI * 1.161, Math.PI * 0.161) ctx.closePath() this._fillStroke(ctx, layer) }, }); var CustomPoint = L.CircleMarker.extend({ _updatePath: function () { this._renderer._updateCustomPoint(this); }, _containsPoint: function(p) { let r = this._radius let insideCircle = p.add([0, r * 2]).distanceTo(this._point) <= r + this._clickTolerance() let a = this._point, b = a.subtract([0.58 * r, r]), c = a.subtract([-0.58 * r, r]) let insideTriangle = true let ap_x = p.x - a.x let ap_y = p.y - a.y let p_ab = (b.x - a.x) * ap_y - (b.y - a.y) * ap_x > 0 let p_ac = (c.x - a.x) * ap_y - (c.y - a.y) * ap_x > 0 let p_bc = (c.x - b.x) * (p.y - b.y) - (c.y - b.y) * (p.x - b.x) > 0 if (p_ac === p_ab) { insideTriangle = false } if (p_bc !== p_ab) { insideTriangle = false } return insideTriangle || insideCircle }, }); var myRenderer; var minZoom = 0, maxZoom = 15; var map = L.map('map', { minZoom: minZoom, maxZoom: maxZoom }).setView([49.79, 19], 15); L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { continuousWorld: false, minZoom: 0, }).addTo(map); myRenderer = L.canvas({ padding: 0.5 }); function style(feature) { return { radius: 13, fillColor: 'green', color: 'white', weight: 1, opacity: 1, fillOpacity: 1, renderer: myRenderer }; } function highlightStyle(feature) { return { radius: 13, fillColor: 'red', color: "#FFCE00", weight: 1, opacity: 1, fillOpacity: 0.9 }; } function resetHighlight(e) { var layer = e.target; dotStyleDefault = style(layer.feature); layer.setStyle(dotStyleDefault); } function highlight(e) { var layer = e.target; dotStyleHighlight = highlightStyle(layer.feature); layer.setStyle(dotStyleHighlight); if (!L.Browser.ie && !L.Browser.opera) { layer.bringToFront(); } } var Features = "https://api.npoint.io/b3db1a36dc59740cb056"; var featureGroup = new L.GeoJSON.AJAX(Features, { onEachFeature: onEachFeature, }).addTo(map); var markerGroup = L.featureGroup().addTo(map); function onEachFeature(feature, layer) { const center = layer.getBounds().getCenter() if (1 == feature.properties.N) { // 创建自定义标记并关联对应feature const customMarker = new CustomPoint(center, style(feature)) customMarker.feature = feature // 单独绑定当前标记的交互事件 customMarker.on({ mouseover: highlight, mouseout: resetHighlight }) // 单独绑定当前标记的专属弹窗 const popup = '<table style="width:110px"><tbody><tr><td><div><b>Marker: </b> </div></td><td><div>' + feature.properties.cena + '</div></td></tr><tr class><td><div><b>Group:</b></div></td><td> <div>' + feature.properties.area + '</div></td></tr><tr><td><div><b>X:</b></div></td><td><div>' + '</div></td></tr><tr><td><div><b>Y:</b></div></td><td><div>' + '</div></td></tr></tbody></table>' customMarker.bindPopup(popup) customMarker.addTo(markerGroup) } }; </script> </body> </html>
内容的提问来源于stack exchange,提问作者Mateusz Kruk
相关产品推荐
相关产品推荐

