小程序的顶部导航栏也就是navigationBar是指,下面图中白色的部分:
它由两部分组成,第一部分为手机的电量、时间等状态那一栏,第二部分就是下面的导航栏的主体部分。不自定义的话,这部分是处于页面之外的,除了默认行为无法操作,且滚动条不会涉及到那部分。但在有些页面,为了更美观,想将导航栏的背景色设置为透明,达到通顶的效果,比如这张设计图:
image.png
而navigationBar可配置的属性并不能达到效果:
image.png
所以我们需要自定义导航栏,先从配置开始:
将navigationStyle属性设置成custom,如果是全局配置,也就是每个页面都采用自定义导航栏,那就在app.json中的window对象中配置,如果是针对个别页面,那就在页面的json文件中配置:
{
"usingComponents": {
},
"navigationStyle": "custom"
}
如果设置了这个属性,看看我们页面现在的效果,先看我现在wxml:
<scroll-view scroll-y class="scroll-view">
<text>超人鸭</text>
</scroll-view>
scroll-view我设置了一个背景色和高度为100vh,现在页面:
可以看到原来的导航栏都不见了,我的页面撑满了整个屏幕,而且包括手机电量那一栏,所以现在就需要自定义一个导航栏,也就是一个组件,我命名为customNavigation:
image.png
在开始编写这个导航栏组件前,先说微信小程序的两个api:
- wx.getSystemInfo,可以获取系统信息,通过它就可以获取手机电量、时间等状态那一栏的高度,示例代码:
wx.getSystemInfo({
success: e => {
console.log(e.statusBarHeight)
}
})
- wx.getMenuButtonBoundingClientRect,获取菜单按钮(右上角胶囊按钮)的布局位置信息。坐标信息以屏幕左上角为原点。示例代码:
let capsule = wx.getMenuButtonBoundingClientRect();
console.log(capsule) // 是个对象,包含宽高,以及四个坐标
因为我们要用到其中的部分属性去设置我们自定义导航栏的宽高以及各种样式。上面说到顶部导航栏其实包括两个部分,一个是手机的状态栏,另一部分才是主体内容,我们的标题、图标等肯定是在主体内容里面。所以在组件里面需要用到手机状态栏的高度来设置padding-top;而右上角的按钮的位置、大小也是无法操作的,所以我们的主要内容应该要避开这个按钮,就需要获取这个胶囊按钮的信息,也就是上面说的两个api。这种全局的信息可以存储到全局变量中,也就是小程序的app.js中:
//app.js
App({
onLaunch: function () {
wx.getSystemInfo({
success: e => {
this.globalData.statusBarHeight = e.statusBarHeight
let windowWidth = e.windowWidth
let capsule = wx.getMenuButtonBoundingClientRect();
this.globalData.capsule = capsule
this.globalData.navigationRightWidth = (windowWidth - capsule.right) * 2 + capsule.width
this.globalData.capsuleToRight = windowWidth - capsule.right
}
})
console.log(this.globalData)
},
globalData: {
statusBarHeight: undefined, // 当前手机顶部状态栏高度,单位px
capsule: {}, // 右上角胶囊的位置信息,宽高以及相对圆点的四个坐标,单位px
navigationRightWidth: undefined, // 胶囊宽度加上两倍胶囊距离右边的距离
capsuleToRight: undefined // 胶囊距离右边的距离
}
})
里面的单位全部都是px,有了这些信息后,开始编写我们的自定义导航组件,首先在customNavigation.js文件中将app中的全局变量赋值到组件的data中:
const appInstance = getApp() // 获取app实例
Component({
// Component的其他属性这里忽略了
data: {
statusBarHeight: appInstance.globalData.statusBarHeight,
capsule: appInstance.globalData.capsule,
navigationRightWidth: appInstance.globalData.navigationRightWidth,
capsuleToRight: appInstance.globalData.capsuleToRight
}
})
然后我们既然把这个导航栏写成一个组件,那通用性就应该高一点,比如导航栏的颜色、文字的颜色、文字的内容都应该由外部定义,导航栏的颜色和文字的颜色可以由属性值传入,文字的内容我们可以用插槽来实现,由外部去定义。为了以后拓展做准备,插槽我使用具名插槽,所以在组件的js文件中添加一个配置,支持多个插槽,所以更改之后的customNavigation.js为:
// compoments/customNavigation.js
const appInstance = getApp()
Component({
options: {
multipleSlots: true // 支持多个插槽
},
/**
* 组件的属性列表
*/
properties: {
bgColor: {
type: String
},
color: {
type: String
}
},
/**
* 组件的初始数据
*/
data: {
statusBarHeight: appInstance.globalData.statusBarHeight,
capsule: appInstance.globalData.capsule,
navigationRightWidth: appInstance.globalData.navigationRightWidth,
capsuleToRight: appInstance.globalData.capsuleToRight
}
})
接下来就是最重要的customNavigation.wxml,我先把代码贴出来:
<view class="navgation-wrapper" style="height:calc(44px + {{statusBarHeight}}px);padding-top:{{statusBarHeight}}px;background:{{bgColor}}">
<view class="navgation-content" style="width:calc(100% - {{navigationRightWidth}}px)">
<view class="title" style="left: calc(50% + {{navigationRightWidth / 2}}px);color:{{color}}">
<slot name="title"></slot>
</view>
</view>
</view>
其中最外层的view就代表整个导航栏,包括手机状态栏,接着下一层的view标签,它代表导航栏的主题部分,宽度已经减去右边胶囊的宽度,在下面就是标题的插槽,里面主要用到calc这个css的属性,里面的单位不仅可以写px,也可以写rpx、vw等其他单位。当然,这个wxml还要结合wxss使用,customNavigation.wxss:
.navgation-wrapper{
position: fixed;
top: 0;
left: 0;
right: 0;
box-sizing: border-box;
transition: all 1s;
}
.navgation-content{
height: 100%;
position: relative;
}
.title{
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
font-size:32rpx;
font-family:PingFangSC-Medium,PingFang SC;
font-weight:bold;
color:rgba(255,255,255,0.85);
line-height:42rpx;
}
接下来就是在页面中使用了,比如我现在的页面叫index,那先需要在index.json中引入这个组件:
{
"usingComponents": {
"custom-navigation": "/components/customNavigation"
},
"navigationStyle": "custom"
}
index.wxml:
<scroll-view scroll-y class="scroll-view">
<custom-navigation bgColor="{{'red'}}" color="{{'#fff'}}">
<text slot="title">自定义导航</text>
</custom-navigation>
</scroll-view>
效果:
到这里,最基础的功能就实现了,接下拓展一下,上面说到有些页面可能要将导航栏置为透明的,然后在页面滚动下来的时候设置为其他颜色,所以现在测试一下,先改一下index.wxml:
<scroll-view scroll-y class="scroll-view" bindscroll="pageScroll">
<custom-navigation bgColor="{{customNavigationBgColor}}" color="{{customNavigationColor}}">
<text slot="title">自定义导航</text>
</custom-navigation>
<!-- 测试滚动 -->
<view style="height:120vh;"></view>
</scroll-view>
接下来在index.js中定义事件:
//index.js
//获取应用实例
const appInstance = getApp()
const statusBarHeight = appInstance.globalData.statusBarHeight
Page({
data: {
customNavigationBgColor: 'transparent',
customNavigationColor:'#fff'
},
pageScroll(event) {
const scrollTop = event.detail.scrollTop
if(scrollTop > (statusBarHeight + 44)) {
this.setData({
customNavigationBgColor: '#fff',
customNavigationColor:'#000'
})
} else {
this.setData({
customNavigationBgColor: 'transparent',
customNavigationColor:'#fff'
})
}
},
onLoad: function () {
},
})
初始效果:
image.png
滚动一定距离后:
image.png
到这里就ok了,实现整个自定义导航栏的就讲完了,当然,所实现的功能还是比较基础,很多情况我还没写进去,比如左上角的回退按钮,这个也可以用插槽来实现。但总体逻辑就是这样,主要在于获取各种设备的位置信息去计算样式。
如果你有更好的做法,欢迎指教哦。
网友评论