1. 为什么放着原生导航栏不用,偏要自己造一个轮子
做小程序的人应该都有一个共同的经历:UI设计稿上的顶部导航栏,跟微信原生导航栏放在一起,怎么看怎么别扭。设计稿里导航栏是品牌色的渐变背景,标题加了字重和字间距,左边是自定义的返回箭头,右边还有一个分享按钮——这些需求用原生导航栏全做不了,或者做出来丑得没法看。
我第一次真正下决心做自定义导航栏,是因为一个商城项目。UI给的设计稿里,首页顶部是半透明的毛玻璃效果,背景图要穿透到导航栏底下,滚动之后慢慢变成实色。原生导航栏只能设置纯色背景,最多配个导航栏渐变,半透明、毛玻璃这种效果想都不用想。另一个刺激点是右上角胶囊按钮,微信那个黑色的胶囊在一堆浅色UI中间特别突兀,如果能把导航栏整体刷成品牌色,胶囊按钮就跟"长"在导航栏里一样,视觉上统一很多。
"自定义顶部导航栏状态栏标题栏"这个需求,在很多uniapp项目里都出现过,尤其是那些对UI还原度要求高的项目。说白了,我们就是把微信或者App系统默认的那条导航栏区域收回来,自己控制状态栏下面的整块空间:状态栏高度留给系统,导航栏标题栏的布局、背景、元素全部由自己的代码决定。
适合做自定义导航栏的场景也很明确:需要品牌色贯穿到顶部、需要背景图延伸到状态栏、需要在导航栏上放超过两个可点击元素(原生最多一个胶囊加一个胶囊左侧按钮,样式还受限)、需要导航栏背景随页面滚动变化,或者干脆就想让标题居中而不是左对齐。如果你只是做一个内部工具类小程序,对UI没有执念,那完全没必要折腾,原生导航栏省事得多。
这个方案的代价也是真实的:你要自己处理状态栏高度、胶囊按钮位置、不同机型的刘海和挖孔,甚至还要考虑安卓虚拟按键对底部的影响。导航栏一旦做错,不是丑的问题,是页面内容会被状态栏遮挡,按钮点不到,甚至直接掉到屏幕外面去。所以这篇文章里我会把整套实现思路、代码、踩过的坑都讲清楚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 自定义导航栏前必须搞懂的两个关键参数:状态栏高度与胶囊按钮位置
2.1 状态栏高度:不是玄学,是系统参数
状态栏就是手机屏幕最顶部显示时间、电量、信号的那一条细长区域。它在iOS和安卓上的表现完全不同,同一系统的不同机型也不一样。
iPhone这边,老机型是20pt,iPhone X之后的刘海屏是44pt,到了iPhone 14 Pro的灵动岛直接变成54pt甚至更高一点;安卓更麻烦,状态栏高度通常是24dp,但不同厂商定制系统会改,有的还做成沉浸式状态栏,让状态栏背景和导航栏背景融为一体。如果你写死一个高度,比如永远取44px,那在老iPhone上页面顶部会多出来24px的空白,在灵动岛上则会发现标题被状态栏文字压住。
在uniapp里获取状态栏高度很简单,核心API是uni.getSystemInfoSync(),它返回一个对象,里面有statusBarHeight字段,单位是px。我一般会在App.vue的onLaunch里拿一次,存到globalData里,后面所有页面直接用,避免每个页面重复调用。
javascript复制// App.vue
onLaunch() {
const systemInfo = uni.getSystemInfoSync()
this.globalData.statusBarHeight = systemInfo.statusBarHeight || 20
this.globalData.systemInfo = systemInfo
}
注意这个值的单位是px,不是rpx。你在写自定义导航栏组件的样式时,要么把px直接用在行内样式上,要么转成rpx。我个人的习惯是状态栏高度和导航栏高度全部用px行内样式控制,因为这是系统返回的物理像素值,直接相加更靠谱,转成rpx反而可能在换算中丢失精度。
2.2 胶囊按钮的几何信息:导航栏高度的核心依据
微信小程序右上角那个胶囊按钮,就是标题栏高度计算的关键。很多人不知道,小程序提供了一个API可以拿到胶囊按钮的精确位置和尺寸:wx.getMenuButtonBoundingClientRect()。
这个API只在微信小程序端存在,它在大部分手机上返回的数据长这样:
json复制{
"width": 87,
"height": 32,
"top": 26,
"right": 365,
"bottom": 58,
"left": 278
}
top是胶囊按钮顶部到屏幕顶部的距离,height是胶囊自身的高度。原生导航栏的标题栏部分,视觉上就是让胶囊按钮上下居中在标题栏中间,所以标题栏高度的计算公式是:
code复制导航栏标题栏高度 = (胶囊按钮.top - 状态栏高度) * 2 + 胶囊按钮.height
这个公式怎么理解?胶囊按钮顶部到状态栏底部的距离是(胶囊.top - 状态栏高度),胶囊按钮底部到标题栏底部的距离如果也等于这个值,那胶囊就正好垂直居中。整个标题栏高度就是上面那段间距加上胶囊高度再加上下面那段间距,也就是(胶囊.top - 状态栏高度) * 2 + 胶囊.height。
我再举个例子。假设一台iPhone 14 Pro,状态栏高度是54px,胶囊的top是68px,height是34px,那标题栏高度就是(68 - 54) * 2 + 34 = 62px。整条自定义导航栏的总高度就是状态栏加标题栏:54 + 62 = 116px。
2.3 没有胶囊按钮的端怎么兜底
问题来了:小程序端有胶囊,但uniapp还会编译到H5和App端,这两个端没
