1.首先,要在json文件中设置为自定义的形式
"navigationStyle": "custom"
2.计算相关值
导航栏分为状态栏和标题栏,只要能算出每台手机的导航栏高度问题就迎刃而解
- 导航栏高度 = 胶囊按钮高度 + 状态栏到胶囊按钮间距 * 2 + 状态栏高度
注:由于胶囊按钮是原生组件,为表现一致,其单位在各种手机中都为 px,所以我们自定义导航栏的单位都必需是 px(切记不能用 rpx),才能完美适配。
因为在不同的手机型号头部那条栏目高度可能不一致,所以为了我们适配更多型号,我们需要计算3个值:
如下图:
1. 整个导航栏的高度;
2. 胶囊按钮与顶部的距离;
3. 胶囊按钮与右侧的距离。
小程序可以通过 wx.getMenuButtonBoundingClientRect() 获取胶囊按钮的信息 和 wx.getSystemInfo() 获取设备信息。
如下图:
通过这些信息我们可以计算出上面说的3个值:
1. 整个导航栏高度 = statausBarHeight + height + (top-statausBarHeight )*2;
2. 胶囊按钮与顶部的距离 = top;
3.胶囊按钮与右侧的距离 = windowWidth - right。
现在我们明白了原理,可以利用胶囊按钮的位置信息和 statusBarHeight 高度动态计算导航栏的高度,贴一个实现此功能最重要的方法
let systemInfo = wx.getSystemInfoSync(); let rect = wx.getMenuButtonBoundingClientRect ? wx.getMenuButtonBoundingClientRect() : null; //胶囊按钮位置信息 wx.getMenuButtonBoundingClientRect(); let navBarHeight = (function() { //导航栏高度 let gap = rect.top - systemInfo.statusBarHeight; //动态计算每台手机状态栏到胶囊按钮间距 return 2 * gap + rect.height; })();
gap 信息就是不同的手机其状态栏到胶囊按钮间距,具体更多代码实现和使用 demo 请移步下方代码仓库,代码中还会有输入框文字跳动解决办法,安卓手机输入框文字飞出解决办法,左侧按钮边框太粗解决办法等等
App.js 代码如下:
App({ globalData: { }, onLaunch: function () { let menuButtonObject = wx.getMenuButtonBoundingClientRect(); wx.getSystemInfo({ success: res => { let statusBarHeight = res.statusBarHeight, navTop = menuButtonObject.top,//胶囊按钮与顶部的距离 navHeight = statusBarHeight + menuButtonObject.height + (menuButtonObject.top - statusBarHeight)*2;//导航高度 this.globalData.navHeight = navHeight; this.globalData.navTop = navTop; this.globalData.windowHeight = res.windowHeight; }, fail(err) { console.log(err); } }) } })
3.因为这个头部导航是公共的,所以我们最好把它设置成一个组件,命名为navbar
index.wxml
<view class="navbar custom-class" style='height:{{navHeight}}px;background-color:{{bgColor}}'> <view wx:if="{{showNav}}" class="navbar-action-wrap navbar-action-group row item-center" style='top:{{navTop}}px;background-color:rgba(255,255,255,.6)'> <ss-icon name="back" color="{{iconColor}}" size="15px" block="{{true}}" class="navbar-action_item" bind:click="_navBack"></ss-icon> <ss-icon name="index" color="{{iconColor}}" size="15px" block="{{true}}" class="navbar-action_item last" bind:click="_toIndex"></ss-icon> </view> <view class='navbar-title' style='top:{{navTop}}px'> {{pageName}} </view> </view>
index.js:
// components/navbar/index.js const App = getApp(); Component({ options: { addGlobalClass: true, }, /** * 组件的属性列表 */ properties: { pageName:String, showNav:{ type:Boolean, value:true }, showHome: { type: Boolean, value: true } }, /** * 组件的初始数据 */ data: { }, lifetimes: { attached: function () { this.setData({ navH: App.globalData.navHeight }) } }, /** * 组件的方法列表 */ methods: { //回退 navBack: function () { wx.navigateBack({ delta: 1 }) }, //回主页 toIndex: function () { wx.navigateTo({ url: '/pages/admin/home/index/index' }) }, } })
index.wxss:
/* components/navbar/index.wxss */ .navbar { 100%; overflow: hidden; position: relative; top: 0; left: 0; z-index: 10; flex-shrink: 0; } .navbar-title { 100%; box-sizing: border-box; padding-left: 115px; padding-right: 115px; height: 32px; line-height: 32px; text-align: center; position: absolute; left: 0; z-index: 10; color: #333; font-size: 16px; font-weight: bold; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .navbar-action-wrap { display: -webkit-flex; display: flex; -webkit-box-align: center; -ms-flex-align: center; -webkit-align-items: center; align-items: center; position: absolute; left: 10px; z-index: 11; line-height: 1; padding-top: 4px; padding-bottom: 4px; } .navbar-action-group { border: 1px solid #f0f0f0; border-radius: 20px; overflow: hidden; } .navbar-action_item { padding: 3px 0; color: #333; } .navbar-action-group .navbar-action_item { border-right: 1px solid #f0f0f0; padding: 3px 14px; } .navbar-action-group .last { border-right: none; }
index.json:
{ "component": true, "usingComponents": { "ss-icon": "../icon/index" } }
组件已创建完毕,现在说下该组件的使用方法:
假设我们需要在index.wxml中需要调用这个组件,
1.在index.json中引用该组件:
{ "usingComponents": { "navbar": "/components/navbar/index" } }
2.在index.wxml中使用该组件:
<view class='view-page'> <navbar page-name="你当前页面的名字"></navbar> <view class='page-content'> <!--这里放你的内容--> </view> </view>
最后的结果如下图所示:
3.参数说明
胶囊信息报错和获取不到
问题就在于 getMenuButtonBoundingClientRect 这个方法,在某些机子和环境下会报错或者获取不到,对于此种情况完美可以模拟一个胶囊位置出来
try { rect = Taro.getMenuButtonBoundingClientRect ? Taro.getMenuButtonBoundingClientRect() : null; if (rect === null) { throw 'getMenuButtonBoundingClientRect error'; } //取值为0的情况 if (!rect.width) { throw 'getMenuButtonBoundingClientRect error'; } //取值为0的情况 if (!rect.width) { throw 'getMenuButtonBoundingClientRect error'; } } catch (error) { let gap = ''; //胶囊按钮上下间距 使导航内容居中 let width = 96; //胶囊的宽度,android大部分96,ios为88 if (systemInfo.platform === 'android') { gap = 8; width = 96; } else if (systemInfo.platform === 'devtools') { if (ios) { gap = 5.5; //开发工具中ios手机 } else { gap = 7.5; //开发工具中android和其他手机 } } else { gap = 4; width = 88; } if (!systemInfo.statusBarHeight) { //开启wifi的情况下修复statusBarHeight值获取不到 systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20; } rect = { //获取不到胶囊信息就自定义重置一个 bottom: systemInfo.statusBarHeight + gap + 32, height: 32, left: systemInfo.windowWidth - width - 10, right: systemInfo.windowWidth - 10, top: systemInfo.statusBarHeight + gap, width }; console.log('error', error); console.log('rect', rect); }