悬浮球必须用 position: fixed 实现视口定位,配合 z-index: 9999 防遮挡;拖拽需 mousedown/mousemove/mouseup 三事件绑定在 document 上,记录偏移量并限制边界,用 requestAnimationFrame 优化性能;移动端须单独处理 touch 事件、加 touch-action: none 和 preventDefault(),并判断设备类型适配。

悬浮球元素必须设置 position: fixed
不加这个,拖拽时位置会随滚动条偏移,看起来“粘不住屏幕”。fixed 让它脱离文档流、相对于视口定位,这是悬浮球的基础。注意别误用 absolute——它相对最近的定位祖先,滚动时会失效。
常见错误:只设 top 和 left,没加 z-index,结果被其他内容遮挡。建议至少设 z-index: 9999,避免和弹窗、导航栏冲突。
示例最小结构:
<div id="floating-btn" style="position: fixed; top: 100px; left: 30px; width: 56px; height: 56px; border-radius: 50%; background: #409eff; z-index: 9999;"></div>
拖拽逻辑要用 mousedown + mousemove + mouseup 三件套
不能只监听 mousemove,否则一进页面就乱动;也不能用 touchstart / touchmove 混着写却忘了 preventDefault(),导致移动端页面跟着拖拽滚动。
立即学习“前端免费学习笔记(深入)”;
关键点:
- 在
mousedown里记录鼠标按下时相对于悬浮球左上角的偏移:offsetX = e.clientX - el.offsetLeft -
mousemove中实时计算:el.style.left = (e.clientX - offsetX) + 'px',同理算top - 必须在
document上绑定mousemove和mouseup,否则鼠标移出按钮就停止响应 - 拖拽结束要清理
document上的监听器,避免内存泄漏(尤其 SPA 页面)
边界限制和防抖不是可选项,是必做项
没加边界判断,拖到屏幕外就找不到了;没做防抖,快速拖拽时 mousemove 频繁触发,UI 卡顿、坐标跳变。
推荐做法:
- 限制
left范围:最小为0,最大为window.innerWidth - el.offsetWidth - 限制
top范围:最小为0,最大为window.innerHeight - el.offsetHeight - 用
requestAnimationFrame包一层mousemove处理函数,比debounce(16)更顺滑 - 移动端额外加
e.preventDefault()在touchmove里,不然 iOS 会触发弹性滚动
移动端适配要单独处理 touch 事件,且不能忽略 touch-action: none
直接复用鼠标事件在手机上基本失效。必须监听 touchstart / touchmove / touchend,并从 e.touches[0] 取坐标。
容易踩的坑:
- 忘了给悬浮球加
style="touch-action: none",系统默认会尝试做双击缩放或滑动,干扰拖拽 -
touchmove不加preventDefault(),页面会跟着手指滚动 - 没处理多点触控(比如两指同时碰),导致坐标错乱;实际只需取
touches[0]就够用 - PC 和移动端共用一套逻辑时,用
'ontouchstart' in window判断设备类型,别靠 UA
拖拽悬浮球看着简单,真正上线前最常翻车的是边界计算误差、移动端 touch 坐标偏移、以及没解绑全局事件导致的内存泄漏——这几个地方多打两行 console.log 看值,比查文档快得多。



















