最常见原因是touchmove被浏览器默认滚动劫持;需给悬浮球容器设touch-action:none,移除-webkit-overflow-scrolling:touch,锁定viewport缩放,并校准DPR坐标换算。

touchmove 事件被 CSS 滚动劫持,导致拖拽坐标中断
悬浮球在触摸屏上拖拽后卡住、松手不复位,最常见原因是 touchmove 事件被浏览器默认滚动行为吞掉,你的 JS 拖拽逻辑收不到连续坐标。尤其当悬浮球父容器或 body 启用了原生滚动(比如 overflow-y: auto 或 -webkit-overflow-scrolling: touch),系统会优先触发滚动,touchmove 的 clientX/clientY 就可能跳变、延迟甚至中断。
验证方式:在拖拽过程中打印 event.touches[0].clientX,如果数值不连续或突然归零,基本就是被劫持了。
- 给悬浮球容器显式加
touch-action: none,彻底禁用该区域的浏览器手势响应 - 避免在
body或根容器设touch-action: manipulation—— 它会一并禁用所有非双击手势 - iOS Safari 12 及更早版本若用了
-webkit-overflow-scrolling: touch,必须移除;现代 iOS 已废弃,保留反而导致 touch 事件丢失
position: fixed 元素在旋转/软键盘弹出后坐标错乱
真机测试时,横竖屏切换或软键盘弹出后悬浮球“飘走”,不是 JS 逻辑问题,而是 fixed 的锚定点(视口)在重绘过程中短暂失效。iOS Safari 和部分 Android WebView 会在旋转瞬间维持旧视口尺寸,直到布局重排完成,此时你存的 left/top 值仍按旧尺寸计算,松手后就复位到错误位置。
关键修复点在 <meta name="viewport">:
立即学习“前端免费学习笔记(深入)”;
- 错误写法:
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> - 正确写法:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"> - 仅靠
user-scalable=no不够,必须同时锁定maximum-scale和minimum-scale,否则浏览器内部缩放逻辑仍会干扰fixed锚定
祖先元素 transform 劫持 fixed 定位上下文
哪怕只是给悬浮球的某个父级加了 transform: translateZ(0) 或 filter: blur(0),也会创建新的包含块(containing block),导致本该相对视口的 fixed 元素,悄悄变成相对这个祖先定位。旋转或软键盘弹出后,该祖先的 transform 状态可能被重算,悬浮球就“跳”到不可预期的位置。
快速排查方法:
- 用开发者工具逐层检查 computed style,看
transform、filter、backdrop-filter、will-change是否为none - 临时加
transform: none !important到可疑祖先上,观察悬浮球是否恢复正常 —— 如果恢复,说明就是它在劫持 - 第三方组件(如轮播图、弹窗)常默认加
transform: translateX(0),你没写,但它写了
复位逻辑依赖 getBoundingClientRect(),但未适配 DPR
很多复位代码用 el.getBoundingClientRect() 获取初始位置,再结合 touch 坐标做差值计算。但 getBoundingClientRect() 返回的是 CSS 像素,而触摸事件的物理坐标需乘以设备像素比(DPR)。Windows 高分屏、iPad Pro 等设备 DPR 常为 1.5 或 2,直接用 CSS 坐标减去物理坐标,会导致复位偏移固定倍数。
正确换算公式是:
物理坐标 = (视口原点CSS + 元素CSS坐标) × DPR
注意:window.screenX / window.screenY 在后台标签页返回 0,不可靠;稳定做法是用 CDP 的 Browser.getWindowBounds 或标定一次前台窗口位置。
真正容易被忽略的,是 DPR 在页面生命周期中可能动态变化(比如用户手动缩放),而多数复位逻辑只在初始化时读一次 window.devicePixelRatio。需要监听 resize 或 orientationchange 事件重新校准。


















