App端悬浮球必须用nvue页面实现,因webview或vue页面无法脱离页面层级全局悬浮;需新建ball.nvue独立文件,通过onLaunch启动并隐藏tabBar,用绝对定位、动态安全区计算、rpx适配、preventDefault防滚动、uni.$emit通信及禁用阴影动画保障流畅性。

App端悬浮球必须用 nvue 页面实现
uni-app 的 webview 或普通 vue 页面在 App 端无法真正脱离页面层级做全局悬浮——系统会把它限制在当前页面容器内,切页即消失或错位。只有 nvue(原生渲染页面)支持通过原生层级创建始终在最上层的视图。
实操建议:
- 新建一个独立的
ball.nvue文件,不挂路由,不进pages.json - 在
App.vue的onLaunch中用uni.navigateTo({ url: '/pages/ball/ball' })启动它,并立即调用uni.hideTabBar({})避免干扰 - 在
ball.nvue里用position: fixed+z-index不起作用,必须用top/right等绝对定位属性 +width/height显式设置 - 别用
v-if控制显隐,改用show变量配合opacity和transform做平滑过渡,否则 nvue 下重绘异常
悬浮球位置拖拽要用 touchmove + getSystemInfoSync 动态限位
直接监听 touchmove 并更新 left/top 会导致拖出屏幕、遮挡状态栏或底部安全区。iOS 和 Android 的可用高度、状态栏高度、底部 home 指示器都不一样。
实操建议:
- 在
onReady中调用uni.getSystemInfoSync()获取screenHeight、statusBarHeight、safeArea等关键值 - 拖拽时实时计算:左边界不能 0,右边界不能 >
screenWidth - 球宽;上边界不能 statusBarHeight,下边界不能 >safeArea.bottom - 球高 - 避免用
px写死尺寸,全部用rpx或动态转为逻辑像素(uni.upx2px(100)) - Android 上
touchmove可能触发默认滚动,加event.preventDefault()(nvue 下有效)
跨页面通信靠 uni.$emit / uni.$on,但要注意生命周期
悬浮球点击后要触发其他页面的逻辑(比如打开客服、跳转首页),不能直接调用目标页面的方法——那些页面可能还没加载,甚至根本没被创建过。
实操建议:
- 在
App.vue的onLaunch和onShow中统一注册全局事件:uni.$on('open-customer-service', handler) - 悬浮球中触发:
uni.$emit('open-customer-service', { from: 'float-ball' }) - 业务页面在
onLoad或onShow中监听,onUnload中uni.$off解绑,否则重复绑定导致多次执行 - 别在
ball.nvue里 import 其他页面模块或调用getCurrentPages()—— nvue 页面拿不到 vue 页面实例
iOS 上悬浮球卡顿/掉帧?关掉 animation 和阴影
nvue 渲染虽快,但 iOS 对 transform + transition 组合非常敏感,尤其叠加 box-shadow 或 blur,极易掉帧。用户一拖就卡,松手还惯性偏移。
实操建议:
- 悬浮球样式禁用
box-shadow、filter: blur()、backdrop-filter - 动画只用
transform: translate(),不用left/top变更(触发布局重排) - 开启硬件加速:给球容器加
style="transform: translateZ(0)" - 测试时连 Xcode 调试,看
Core Animation面板 FPS 是否稳定在 58–60
最麻烦的其实是多点触控干扰和长按误触发——这些细节不写死逻辑,上线后用户一试就露馅。


















