移动端地图手势冲突与滚动穿透
手机上想拖地图,结果整页跟着滚;想双指缩放,页面被放大;地图在页面中间,永远滚不过去。
拖地图带动页面滚动
浏览器默认把单指拖动当作页面滚动。地图库虽然阻止了默认行为,
但前提是它的事件监听能拿到触摸事件并被允许调用 preventDefault。
如果外层容器设了 touch-action: pan-y 之类的属性,
浏览器会先接管手势,地图就拿不到了。
解决办法是给地图容器明确声明它自己处理所有手势:
#map {
touch-action: none;
}
touch-action: none 会让容器内所有手势都交给 JS 处理,
页面在这个区域不再滚动。代价是用户在地图上没法滚动页面,
所以地图不应该占据整个屏幕高度的全部空间,要留出可滚动的区域。
双指缩放变成页面缩放
需要同时做两件事:禁用页面级的双指缩放,以及确保地图库能收到双指事件。
<meta name="viewport"
content="width=device-width, initial-scale=1,
maximum-scale=1, user-scalable=no">
user-scalable=no 会影响可访问性,只在对交互精度要求高的地图页使用。
更温和的做法是保留页面缩放,只给地图容器加 touch-action: none。
滚到地图位置就卡住
地图占满一屏宽度、又吃掉了所有手势,用户的手指落在上面就无法纵向滚动页面。 这是「地图墙」问题,在移动端非常影响体验。
| 做法 | 说明 |
|---|---|
| 限制地图高度 | 不超过视口高度的一半,留出滚动空间 |
| 单指滚动、双指操作地图 | Leaflet 可配置拖拽手势的判定条件 |
| 加「展开」按钮 | 默认静态图,点击后进入全屏交互模式 |
Leaflet 的手势配置
var map = L.map('map', {
dragging: !L.Browser.mobile, // 移动端关闭单指拖拽
tap: false,
touchZoom: true
});
// 移动端允许双指拖动地图
if (L.Browser.mobile) {
map.dragging.enable();
}
更精细的控制可以给 dragging 加自定义判定,
比如只有在双指按下时才允许拖动,单指留给页面滚动。
地图在弹层里滚动穿透
全屏地图弹层打开后,手指在地图外拖动,背后的页面仍在滚动。
这是滚动穿透,需要在弹层打开时锁住 body。
function lockScroll(on) {
document.body.style.overflow = on ? 'hidden' : '';
// iOS 需要额外处理
document.body.style.position = on ? 'fixed' : '';
document.body.style.width = on ? '100%' : '';
}
iOS Safari 对 overflow: hidden 不生效,必须配合
position: fixed。但这样会丢失滚动位置,
需要在解锁时恢复 scrollTop。
点击延迟与 300 毫秒
老版本移动浏览器为了判断双击缩放,会在 click 前等 300 毫秒。
现在的主流浏览器在有正确 viewport 设置时已经取消了这个延迟。
如果还遇到点击迟钝,检查 viewport 是否写了 width=device-width。
排查顺序
- 确认地图容器有没有
touch-action限制 - 确认地图高度是否占满整屏导致无法滚动
- 区分是「页面缩放」还是「地图缩放」,前者改 viewport,后者查手势配置
- 弹层场景检查 body 是否锁住了滚动
- 真机测试,不要只用浏览器模拟器
经纬志(2026)。《移动端地图手势冲突与滚动穿透》。https://www.jingweizhi.com/cookbook/mobile-gesture.html(访问日期 2026-11-03)。