uniapp 自定义导航栏:胶囊按钮 + 状态栏适配,别再用默认导航了
导读:小程序默认导航栏就一个标题,想放搜索框、自定义按钮、渐变背景全都得靠自定义。我把 job-uniapp 的导航栏改成了自定义,中间踩了胶囊位置测量、状态栏高度、页面滚动吸顶一堆坑。这篇把完整方案写清楚。
为什么改自定义导航栏?因为产品要一个"带搜索框 + 城市切换"的首页头部,默认导航栏塞不进去。uniapp 里把pages.json的页面navigationStyle设成custom,整页就全屏了,导航栏自己画。
第一步:关闭默认导航栏
{"pages":[{"path":"pages/index/index","style":{"navigationStyle":"custom","navigationBarTitleText":"小蓝直聘"}}]}注意navigationStyle: "custom"只在 App 端和小程序端生效,H5 端这个配置无效,H5 默认就是自定义的(没有原生导航栏概念),但 H5 也没有胶囊按钮,后面适配要区分。
第二步:获取胶囊按钮位置和状态栏高度
**自定义导航栏最难的是对齐右上角那个胶囊按钮(小程序端),以及避开刘海屏。**uniapp 提供了两个 API:
// 状态栏高度(刘海屏区域高度)constsystemInfo=uni.getSystemInfoSync();conststatusBarHeight=systemInfo.statusBarHeight;// 胶囊按钮位置(仅小程序端有)constmenuButtonInfo=uni.getMenuButtonBoundingClientRect();注意getMenuButtonBoundingClientRect()只在微信小程序里有,uni.getMenuButtonBoundingClientRect()在 uniapp 里也有封装,但 H5/App 端没有这个原生概念,要兼容判断。
导航栏高度算法:
// 导航栏总高度 = 状态栏高度 + 胶囊按钮高度 + 上下留白constnavBarHeight=(menuButtonInfo.top-statusBarHeight)*2+menuButtonInfo.height;这个公式的含义:胶囊按钮顶部到状态栏底部的距离就是导航栏上下留白的一半,所以(top - statusBarHeight) * 2 + height就是导航栏完整高度。
第三步:封装 nav-bar 组件
我用一个自定义组件统一处理,页面里直接引用:
export default { name: 'NavBar', props: { showBack: { type: Boolean, default: false } }, data() { return { statusBarHeight: 20, menuButtonHeight: 32 } }, computed: { contentHeight() { return this.menuButtonHeight }, navBarHeight() { return this.statusBarHeight + this.menuButtonHeight + 8 } }, created() { const sys = uni.getSystemInfoSync() this.statusBarHeight = sys.statusBarHeight || 20 // #ifdef MP-WEIXIN const menu = uni.getMenuButtonBoundingClientRect() if (menu && menu.height) { this.menuButtonHeight = menu.height this.navBarTotal = (menu.top - this.statusBarHeight) * 2 + menu.height } // #endif } }条件编译#ifdef MP-WEIXIN保证只有小程序端才取胶囊位置,H5 和 App 端用默认值。
踩坑记录
坑一:created 里拿不到胶囊按钮位置
现象:真机上导航栏高度算出来不对,胶囊按钮和标题错位。排查发现getMenuButtonBoundingClientRect()在onLoad/created早期调用返回全 0。
定位思路:这个 API 依赖页面渲染完成,组件 created 阶段 DOM 还没就绪。解决:放到onReady或者用setTimeout延迟调用。更稳的做法是在 App.vue 的 onLaunch 里提前获取一次缓存起来,所有页面复用。
// App.vueonLaunch(){// #ifdef MP-WEIXINconstmenu=uni.getMenuButtonBoundingClientRect()uni.$globalData={menuButtonInfo:menu,statusBarHeight:uni.getSystemInfoSync().statusBarHeight}// #endif}坑二:H5 端没有胶囊按钮,导航栏右边空一块
现象:H5 打开页面,导航栏右侧空着,胶囊区域占位导致标题偏左。解决:H5 端不预留胶囊空间,导航栏右侧放正常的操作按钮。
坑三:页面滚动时导航栏不吸顶
现象:自定义导航栏是页面的一部分,页面滚动导航栏跟着滚走了。解决:给导航栏加position: fixed; top: 0; left: 0; right: 0; z-index: 999,同时给页面内容加padding-top等于导航栏高度,不然内容会被遮住。
坑四:自定义导航栏后返回按钮失效
现象:关闭默认导航栏后,左上角返回按钮没了,用户点了页面里的返回没反应。解决:uni.navigateBack()之前判断getCurrentPages().length,只有一页时用uni.switchTab或uni.reLaunch回首页。
可直接复用
navigationStyle: "custom"关默认导航栏,注意 H5 端无效- 导航栏高度公式:
(胶囊.top - 状态栏高度) * 2 + 胶囊.height - 胶囊位置在
onReady后取,或 App.vue onLaunch 里缓存 - 导航栏
position: fixed吸顶,页面内容补padding-top - 条件编译区分小程序/H5,H5 无胶囊按钮
- 返回逻辑判断页面栈深度,只有一页走 reLaunch
**自定义导航栏这块细节特别多,机型差异一测一个准。**job-uniapp(小蓝直聘)里这套组件已经是兼容好的版本,直接抄作业就行。
项目源码:https://gitee.com/gzqkl/job-uniapp