[打怪升级]小程序自定义头部导航栏“完美”解决方案
[图片]
为什么要做这个?
主要是在项目中,智酷君发现的一些问题
一些页面是通过扫码和订阅消息访问后,没有直接可以点击去首页的,需要添加一个home链接
需要添加自定义搜索功能
需要自定义一些功能按钮
[图片]
其实,第一个问题,在最近的微信版本更新中已经优化了,通过 小程序模板消息 过来的,系统会自动加上home按钮,但对于其他的访问方式则没有支持~
一个不大不小的问题:两边ICON不对齐问题
[图片]
智酷君之前尝试了各种解决方法,发现有一个问题,就是现在手机屏幕太多种多样,有 传统头部、宽/窄刘海屏、水滴屏等等,无法八门,很多解决方案都无法解决特殊头部,系统**“胶囊按钮”** 和 自定义按钮在Android屏幕可能有 几像素不对齐 的问题(强迫症的噩梦)。
下面分享下一个相对比较完善的解决方案:
[图片]
小程序代码段DEMO
Link: https://developers.weixin.qq.com/s/cuUaCimT72cH
ID: cuUaCimT72cH
智酷君做了一个demo代码段,方便大家直接用IDE工具查看源码~
[图片]
页面配置
1、页面JSON配置
[代码]{
"usingComponents": {
"NavComponent": "/components/nav/common" //以插件的方式引入
},
"navigationStyle": "custom" //自定义头部需要设置
}
[代码]
如果需要自定义头部,需要设置navigationStyle为 “custom”
2、页面代码
[代码]<!-- home 类型的菜单 -->
<NavComponent v-title="自定义头部" bind:commonNavAttr="commonNavAttr"></NavComponent>
<!-- 搜索菜单 -->
<NavComponent is-search="true" bind:commonNavAttr="commonNavAttr"></NavComponent>
[代码]
可以在自定义导航标签上添加属性配置来设置功能,具体按照实际需要来
3、目录结构
[代码]│
├─components
│ └─nav
│ common.js
│ common.json
│ common.wxml
│ common.wxss
│
├─images
│ back.png
│ home.png
│
└─index
index.js
index.json
index.wxml
index.wxss
search.js
search.json
search.wxml
search.wxss
[代码]
仅供参考
插件对应的JS部分
components/nav/common.js部分
[代码]const app = getApp();
Component({
properties: {
vTitle: {
type: String,
value: ""
},
isSearch:{
type: Boolean,
value: false
}
},
data: {
haveBack: true, // 是否有返回按钮,true 有 false 没有 若从分享页进入则没有返回按钮
statusBarHeight: 0, // 状态栏高度
navbarHeight: 0, // 顶部导航栏高度
navbarBtn: { // 胶囊位置信息
height: 0,
width: 0,
top: 0,
bottom: 0,
right: 0
},
cusnavH: 0, //title高度
},
// 微信7.0.0支持wx.getMenuButtonBoundingClientRect()获得胶囊按钮高度
attached: function () {
if (!app.globalData.systeminfo) {
app.globalData.systeminfo = wx.getSystemInfoSync();
}
if (!app.globalData.headerBtnPosi) app.globalData.headerBtnPosi = wx.getMenuButtonBoundingClientRect();
console.log(app.globalData)
let statusBarHeight = app.globalData.systeminfo.statusBarHeight // 状态栏高度
let headerPosi = app.globalData.headerBtnPosi // 胶囊位置信息
console.log(statusBarHeight)
console.log(headerPosi)
let btnPosi = { // 胶囊实际位置,坐标信息不是左上角原点
height: headerPosi.height,
width: headerPosi.width,
top: headerPosi.top - statusBarHeight, // 胶囊top - 状态栏高度
bottom: headerPosi.bottom - headerPosi.height - statusBarHeight, // 胶囊bottom - 胶囊height - 状态栏height (胶囊实际bottom 为距离导航栏底部的长度)
right: app.globalData.systeminfo.windowWidth - headerPosi.right // 这里不能获取 屏幕宽度,PC端打开小程序会有BUG,要获取窗口高度 - 胶囊right
}
let haveBack;
if (getCurrentPages().length != 1) { // 当只有一个页面时,并且是从分享页进入
haveBack = false;
} else {
haveBack = true;
}
var cusnavH = btnPosi.height + btnPosi.top + btnPosi.bottom // 导航高度
console.log( app.globalData.systeminfo.windowWidth, headerPosi.width)
this.setData({
haveBack: haveBack, // 获取是否是通过分享进入的小程序
statusBarHeight: statusBarHeight,
navbarHeight: headerPosi.bottom + btnPosi.bottom, // 胶囊bottom + 胶囊实际bottom
navbarBtn: btnPosi,
cusnavH: cusnavH
});
//将实际nav高度传给父类页面
this.triggerEvent('commonNavAttr',{
height: headerPosi.bottom + btnPosi.bottom
});
},
methods: {
_goBack: function () {
wx.navigateBack({
delta: 1
});
},
bindKeyInput:function(e){
console.log(e.detail.value);
}
}
})
[代码]
解决不同屏幕头部不对齐问题的终极办法是 wx.getMenuButtonBoundingClientRect()
这个方法从微信7.0.0开始支持,通过这个方法我们可以获取到右边系统胶囊的top、height、right等属性,这样无论是水滴屏、刘海屏、异形屏,都能完美对齐右边系统默认的胶囊bar,完美治愈强迫症~
APP.js 部分
[代码]//app.js
App({
/**
* 加载页面
* @param {*} options
*/
onShow: function (options) {
},
onLaunch: async function () {
let self = this;
//设置默认分享
this.globalData.shareData = {
title: "智酷方程式"
}
// this.getSysInfo();
},
globalData: {
//默认分享文案
shareData: {},
qrCodeScene: false, //二维码扫码进入传参
systeminfo: false, //系统信息
headerBtnPosi: false, //头部菜单高度
}
});
[代码]
将获取的参数存储在一个全局变量globalData中,可以减少反复调用的性能消耗。
插件HTML部分
[代码]<view class="custom_nav" style="height:{{navbarHeight}}px;">
<view class="custom_nav_box" style="height:{{navbarHeight}}px;">
<view class="custom_nav_bar" style="top:{{statusBarHeight}}px; height:{{cusnavH}}px;">
<!-- 搜索部分-->
<block wx:if="{{isSearch}}">
<input class="navSearch"
style="height:{{navbarBtn.height-2}}px;line-height:{{navbarBtn.height-4}}px; top:{{navbarBtn.top+1}}px; left:{{navbarBtn.right}}px; border-radius:{{navbarBtn.height/2}}px;"
maxlength="10" bindinput="bindKeyInput" placeholder="输入文字搜索" />
</block>
<!-- HOME 部分-->
<block wx:else>
<view class="custom_nav_icon {{!haveBack||'borderLine'}}"
style="height:{{navbarBtn.height}}px;line-height:{{navbarBtn.height-2}}px; top:{{navbarBtn.top}}px; left:{{navbarBtn.right}}px; border-radius:{{navbarBtn.height/2}}px;">
<view wx:if="{{haveBack}}" class="icon-back" bindtap='_goBack'>
<image src='/images/back.png' mode='aspectFill' class='back-pre'></image>
</view>
<view wx:if="{{haveBack}}" class='navbar-v-line'></view>
<view class="icon-home">
<navigator class="home_a" url="/pages/home/index" open-type="switchTab">
<image src='/images/home.png' mode='aspectFill' class='back-home'></image>
</navigator>
</view>
</view>
<view class="nav_title" style="height:{{cusnavH}}px; line-height:{{cusnavH}}px;">
{{vTitle}}
</view>
</block>
</view>
</view>
</view>
[代码]
主要是对几种状态的判断和定位的计算。
插件CSS部分
[代码]/* components/nav/test.wxss */
.custom_nav {
width: 100%;
background: #3a7dd7;
position: relative;
z-index: 99999;
}
.custom_nav_box {
position: fixed;
width: 100%;
background: #3a7dd7;
z-index: 99999;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
}
.custom_nav_bar {
position: relative;
z-index: 9;
}
.custom_nav_box .nav_title {
font-size: 28rpx;
color: #fff;
text-align: center;
position: absolute;
max-width: 360rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
z-index: 1;
}
.custom_nav_box .custom_nav_icon {
position:absolute;
z-index: 2;
display: inline-block;
border-radius: 50%;
vertical-align: top;
font-size:0;
box-sizing: border-box;
}
.custom_nav_box .custom_nav_icon.borderLine {
border: 1rpx solid rgba(255, 255, 255, 0.3);
background: rgba(0, 0, 0, 0.1);
}
.navbar-v-line {
width: 1px;
margin-top: 14rpx;
height: 32rpx;
background-color: rgba(255, 255, 255, 0.3);
display: inline-block;
vertical-align: top;
}
.icon-back {
display: inline-block;
width: 74rpx;
padding-left: 20rpx;
vertical-align: top;
/* margin-top: 12rpx;
vertical-align: top; */
height: 100%;
}
.icon-home {
/* margin-top: 8rpx;
vertical-align: top; */
display: inline-block;
width: 80rpx;
text-align: center;
vertical-align: top;
height: 100%;
}
.icon-home .home_a {
height: 100%;
display: inline-block;
vertical-align: top;
width: 35rpx;
}
.custom_nav_box .back-pre,
.custom_nav_box .back-home {
width: 35rpx;
height: 35rpx;
vertical-align: middle;
}
.navSearch {
width: 200px;
background: #fff;
font-size: 14px;
position: absolute;
padding: 0 20rpx;
z-index: 9;
}
[代码]
总结:
通过微信API: getMenuButtonBoundingClientRect(),结果各类手机屏幕的适配问题
将算好的参数存储在全局变量中,一次计算全局使用,爽YY~
往期回顾:
[填坑手册]小程序PC版来了,如何做PC端的兼容?!
[填坑手册]小程序Canvas生成海报(一)
[拆弹时刻]小程序Canvas生成海报(二)