app端必须用plus.nativeobj.view初始化悬浮按钮,禁用v-if控制显隐;小程序/h5分别用movable-area和fixed定位;需按平台分离逻辑并同步本地存储状态。

App端必须用 plus.nativeObj.View,别碰 v-if 或 show
App端(iOS/Android)的原生渲染层不支持跨页面 Vue 组件挂载。一旦你在某个页面用 v-if="false" 卸载了悬浮按钮组件,它就从原生视图树里彻底消失了——切页后不会自动复活,所谓“全局”直接失效。
常见错误现象:onShow 里 this.show = true 没反应、按钮在首页显示,进二级页就消失、真机调试时偶尔闪一下就没了。
- 必须在
App.vue的onLaunch中初始化,且只执行一次 - 显隐控制只能调用
view.show()/view.hide(),不能依赖 Vue 响应式 - 点击事件必须用
view.addEventListener('click', ...),@click无效 - 不要把按钮写在
pages.json的每个页面里,也不要在各页面mounted中重复创建
小程序和 H5 用 movable-area + movable-view 最稳
这是 uni-app 官方为跨平台拖拽设计的原生抽象组件,底层调用各端原生能力,性能远高于手动监听 @touchmove 计算位移。Web 端靠 CSS position: fixed 会卡顿、穿透、被软键盘顶飞;小程序里 movable-view 是唯一能保证边缘吸附+流畅拖拽的方案。
-
movable-area必须设position: fixed; width: 100%; height: 100%,否则拖拽区域错位 -
pointer-events: none加在movable-area上,避免遮挡底层页面点击 -
movable-view内部内容要设pointer-events: auto,否则点不动 - 初始位置建议在
mounted里用uni.getSystemInfo获取真实屏幕宽高再计算,别用固定像素值
自动吸附逻辑别硬写 if-else,先算中心偏移再判断
很多实现直接比 lastX > screenWidth / 2,结果在窄屏手机上吸附不准、边缘留白大。正确做法是把按钮当圆心,算出当前坐标到左右/上下边缘的距离,取最小值后做阈值判断。
- 假设按钮宽高为
size,则有效吸附范围是:左边缘x 、右边缘 <code>x > screenWidth - size / 2 - 松手瞬间取
lastX和lastY,不是currentX(后者可能被 damping 拉回) - 吸附动画用
uni.animate或 CSS transition 更平滑,别直接赋值x = screenWidth - size - 安卓真机上
movable-view的damping值建议设为 30~50,太小拖拽发飘,太大回弹迟钝
跨平台统一入口,但得按端分离初始化逻辑
一个项目常需同时支持微信小程序、App、H5,但三端底层机制完全不同:小程序靠 movable-view,App 靠 plus.nativeObj.View,H5 靠 position: fixed + transform。强行一套代码适配只会处处打补丁。
- 在
App.vue的onLaunch里用uni.getSystemInfoSync().platform判断当前环境 - App 端走
plus.nativeObj.View创建路径,其他端走movable-area组件或 DOM 操作 - 不要在组件内用
process.env.UNI_PLATFORM === 'app'做条件编译——这在运行时不可靠,尤其 App 端 native 层根本没这个变量 - H5 端注意监听
resize事件重算位置,PC 浏览器窗口缩放会导致按钮错位
实际最难处理的是 App 端的生命周期同步:比如用户在客服对话页点了“隐藏按钮”,切到设置页又想让它出现——这时候不能只靠 Vue 数据驱动,得把状态存在 uni.setStorageSync,并在每次 view.show() 前检查缓存。否则原生视图还在,但业务逻辑已经认为它该隐藏了。











