小程序自定义导航栏
小程序布局
谈到导航栏与自定义导航栏,就需要解释一下微信小程序的布局了。在小程序开发中使用wx.getSystemInfoAsync() 方法可以获取到系统信息。
部分获取到的信息如上图(截取自微信小程序开发者文档),对我们理解布局有用的信息是以上跟宽度高度相关的属性,如当前设备的屏幕高宽,可用高宽,以及saveArea。
上图展示我们从systemInfo获取到的数据的实际表现,以苹果X的刘海屏为例(所有安卓刘海屏原理类似):最外层的红色框即屏幕大小,蓝色框即安全区域字面意思也就是开发者所能操纵的页面区域,上面的黄色框即手机的状态栏,绿色区域即我们要自定义的navigationBar。
可见,导航栏紧贴safeArea的上部,如果使用原生导航栏,导航栏下方即是真正意义的可操控范围。
实际上我们自定义的导航栏也是在这个safeArea内与胶囊对齐最为和谐。很关键的原因就是微信将右上角的胶囊按钮作为了内置组件,只有黑白两种颜色,即我们无法改变它的大小位置透明度等等,所以为了配合胶囊按钮,一般自定义的导航栏位置也与上图位置一致。
自定义navigationBar怎么做?
去掉原生导航栏。
- 将需要自定义navigationBar页面的page.json的navigationBarTitleText去掉。
- 加上 "navigationStyle":"custom" ,这样原生的导航栏就已经消失,甚至后退键也不会出现需要自定义。
- 另外,早在2016年微信已经开始适配沉浸式状态栏,目前几乎所有的机型里微信都是沉浸式状态栏,也就是说去掉原生导航栏的同时,整个屏幕已经成为可编程区域。
计算navigationBarHeight。
- 原生的胶囊按钮当然存在,那么下一步就需要你去定位出自定义的导航栏高度以及位置。
- 对于不同的机型,对于不同的系统,状态栏以及胶囊按钮的位置都不确定,所以需要用到一定的计算,从而面对任何机型都可以从容判定。
使用wx.getSystemInfoSync() 获取到statusBarHeight,这样就确定了导航栏最基本的距离屏幕上方的距离。
使用wx.getMenuButtonBoundingClientRect() 获取到小程序的胶囊信息(注意这个api存在各种问题,在不同端表现不一致,后面会叙述这个api调用失败的处理情况),如下图,以下坐标信息以屏幕左上角为原点。
以下图为例,上面的红色框是statusBar,高度已知;下面的红色框是正文内容,夹在中间的就是求解之一navigationBarHeight;而黄色的是原生胶囊按钮也是在垂直居中位置,高度为胶囊按钮基于左上角的坐标信息已知,不难得出,navigationBarHeight = 蓝色框高度 × 2 + 胶囊按钮.height。(蓝色框高度 = 胶囊按钮.top - statusBarHeight)
- 最后的计算公式为:navigationBarHeight = (胶囊按钮.top - statusBarHeight) × 2 + 胶囊按钮.height。navigationBar 距屏幕上方的距离即为navigationBarHeight。
- 这种计算方法在各种机型以及安卓ios都适用。
- 针对"wx.getMenuButtonBoundingClientRect() "获取错误或者获取数据为0的极少数情况,只能够去模拟,对于android,一般navigationBarHeight为48px,而对于ios一般为40px,所有机型的胶囊按钮高度是32px。
代码实现
- 获取本机信息,写在组件的attached生命周期中。
// components/Navigation/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
navigationBarHeight: 40,
statusBarHeight:20,
},
/**
* 组件的方法列表
*/
methods: {
},
lifetimes: {
attached: function () {
const { statusBarHeight, platform } = wx.getSystemInfoSync();
const { top, height = 32 } = wx.getMenuButtonBoundingClientRect();// 胶囊按钮高度 一般是32 如果获取不到就使用32
// 判断胶囊按钮信息是否成功获取
if (top && top !== 0 && height && height !== 0) {
//获取成功进行计算
const navigationBarHeight = (top - statusBarHeight) * 2 + height;
console.log(navigationBarHeight)
// 导航栏高度
this.setData({
navigationBarHeight,
statusBarHeight
})
} else {
//获取失败使用默认的高度
this.setData({
navigationBarHeight: platform === "android" ? 48 : 40,
statusBarHeight
})
}
}
}
})
- 组件模板编写
<view class="custom-nav" style="height: {{navigationBarHeight}}px;margin-top:{{statusBarHeight}}px;">
<view>
<image style="width: 40rpx;height:40rpx;" src="/images/location.svg" mode="" />
</view>
</view>
.navigationBar.wxml 样式如下:
.custom-nav{
background-color:palegoldenrod;
display: flex;
align-items: center;
}
.custom-nav__title{
margin:auto
}
外部页面引用该组件如下,
.json文件,引入组件
{
"usingComponents": {
"my-navigation":"/components/Navigation"
},
"navigationStyle": "custom"
}
注意添加属性:"navigationStyle":"custom" 代表我们要自定义组件
.wxml代码如下:
<view>
<my-navigation></my-navigation>
<view class="page-container" style="background-color: rebeccapurple;">这里是页面内容</view>
</view>
最终效果
如果想要编写更加通用的组件,可以根据需求定义传入的参数和样式
参考链接
来源:juejin.cn/post/7254812719349858361