Webflow CMS+Leaflet实现按城市距离筛选房产及范围圈绘制需求
基于Leaflet的城市范围房源筛选与圈选实现方案
核心逻辑拆解
要实现目标功能,需完成三个关键环节:
- 计算房源坐标与选定城市的球面距离(公里)
- 在Leaflet地图上绘制对应半径的范围圈
- 联动筛选房源元素与地图标记,仅保留圈内内容
1. 封装球面距离计算函数(Haversine公式)
Leaflet未直接提供公里级球面距离计算,用Haversine公式可精准计算地球表面两点间距离:
// 计算两个坐标点的球面距离(单位:公里) function calculateDistance(lat1, lon1, lat2, lon2) { const R = 6371; // 地球半径(公里) const dLat = (lat2 - lat1) * Math.PI / 180; const dLon = (lon2 - lon1) * Math.PI / 180; const a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon/2) * Math.sin(dLon/2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); return R * c; // 返回公里数 }
2. 绑定城市与距离选择器的事件监听
假设使用下拉框选择城市(#city-select)和输入框设置距离(#distance-input),将选择逻辑与地图、房源筛选联动:
// 全局变量存储地图实例和范围圈 let mapInstance; let radiusCircle = null; // DOM加载完成后初始化 document.addEventListener('DOMContentLoaded', function() { // 替换为你现有代码中初始化Leaflet地图的逻辑,获取map实例 mapInstance = L.map(document.querySelector('[data-geo-map]')).setView([51.505, -0.09], 13); const citySelect = document.getElementById('city-select'); const distanceInput = document.getElementById('distance-input'); function handleFilterChange() { const selectedCity = citySelect.value; // 假设选项值为"lat,lng"格式坐标 const maxDistance = parseFloat(distanceInput.value) || 0; if (!selectedCity || maxDistance <= 0) { resetFilter(); return; } // 解析城市坐标 const [cityLat, cityLng] = selectedCity.split(',').map(Number); // 绘制/更新范围圈 updateRadiusCircle(cityLat, cityLng, maxDistance); // 筛选房源和标记 filterPropertiesByDistance(cityLat, cityLng, maxDistance); } citySelect.addEventListener('change', handleFilterChange); distanceInput.addEventListener('input', handleFilterChange); });
3. 绘制/更新范围圈
function updateRadiusCircle(cityLat, cityLng, maxDistance) { // 清除已有的范围圈 if (radiusCircle) { mapInstance.removeLayer(radiusCircle); } // 公里转米(Leaflet的Circle默认单位是米) const radiusInMeters = maxDistance * 1000; // 绘制新范围圈 radiusCircle = L.circle([cityLat, cityLng], { color: '#2563eb', fillColor: '#3b82f6', fillOpacity: 0.1, radius: radiusInMeters }).addTo(mapInstance); // 调整地图视野适配范围圈 mapInstance.fitBounds(radiusCircle.getBounds()); }
4. 筛选圈内房源与地图标记
结合Webflow的data-geo-place和data-geo-coordinates属性,联动Finsweet筛选逻辑:
function filterPropertiesByDistance(cityLat, cityLng, maxDistance) { // 遍历所有房源元素 document.querySelectorAll('[data-geo-place]').forEach(placeEl => { // 解析房源坐标 const [placeLat, placeLng] = placeEl.dataset.geoCoordinates.split(',').map(Number); const distance = calculateDistance(cityLat, cityLng, placeLat, placeLng); // 控制房源显示/隐藏,匹配现有Finsweet筛选逻辑 if (distance <= maxDistance) { placeEl.style.display = 'block'; // 显示对应地图标记(需确保房源与标记通过data属性关联,如data-place-id) const markerId = placeEl.dataset.placeId; const marker = mapInstance._layers[markerId]; if (marker) marker.addTo(mapInstance); } else { placeEl.style.display = 'none'; const markerId = placeEl.dataset.placeId; const marker = mapInstance._layers[markerId]; if (marker) mapInstance.removeLayer(marker); } }); // 若需联动Finsweet筛选,可触发其更新事件(根据官方文档调整) // document.dispatchEvent(new CustomEvent('fs-cmsfilter-reset')); } // 重置筛选状态 function resetFilter() { if (radiusCircle) { mapInstance.removeLayer(radiusCircle); radiusCircle = null; } document.querySelectorAll('[data-geo-place]').forEach(placeEl => { placeEl.style.display = 'block'; const markerId = placeEl.dataset.placeId; const marker = mapInstance._layers[markerId]; if (marker) marker.addTo(mapInstance); }); }
关键注意事项
- 确保
data-geo-coordinates格式为纬度,经度(如51.505,-0.09),且数值有效 - 房源元素与地图标记需通过唯一标识关联(如
data-place-id),实现精准控制 - 若Finsweet筛选有自定义样式逻辑,需调整显示/隐藏方式以兼容现有代码
- 可根据UI需求修改范围圈的颜色、透明度等参数
内容的提问来源于stack exchange,提问作者Liam LEGAILLARD
相关产品推荐
相关产品推荐

