前言
使用uniapp开发微信小程序时,小程序右上角会有一个胶囊按钮,如果随便定义自定义的导航栏的高度,就会出现被遮盖的情况,特别是ios13+的手机下。理想情况是导航栏标题与胶囊按钮对齐。
布局
导航是需要占位的但是又是需要固定在头部的,以便滚动时导航栏固定不动。这里就可以拆分为两个内容,一个用于dom正常流式布局占位,一个用于固定显示。
// BaseNavigation.vue
<view class="navigation-box">
<view class="fixed-navigation">
<view class="navigation">
<view class="navigation-container">
<view class="left">
<image class="icon" @click="onBack" :src="`/static/images/public/back.png`"/>
</view>
<view class="center">{{ title }}</view>
<view class="right"></view>
</view>
</view>
</view>
</view>
navigation-box 最外层的也是占位的 它只需要有个高度即可
fixed-navigation 固定定位的元素,背景色的设置
navigation 设置内容高度
navigation-container 内容所在区域,返回按钮 标题
如何获取准确的高度对导航栏设置
通过 uni.getMenuButtonBoundingClientRect() 可以获取胶囊按钮的 top bottom height width left 等属性,利用这个api即可进行对应高度计算。
// navigationMixin.js
const navigationMixin = {
data: function() {
return {
WXBUTTONINFO: {},
WXNAVIGATIONGAP: 8, // 距离胶囊按钮底部的间隙px,
navigationHeight: 0
};
},
created: function() {
// #ifdef MP-WEIXIN
this.WXBUTTONINFO = uni.getMenuButtonBoundingClientRect();
// #endif
// #ifdef H5
this.WXBUTTONINFO = { height: 30, width: 100, top: 50, bottom: 80, left: 300 };
// #endif
this.navigationHeight = this.WXBUTTONINFO.bottom + this.WXNAVIGATIONGAP; // 直接获取按钮底部位置 + 间隙
},
computed: {
computedNavigationStyle: function() { // 导航栏总高度
return `height: ${this.navigationHeight}px`;
},
computedNavigationContainerStyle: function() {
const { top, height } = this.WXBUTTONINFO;
return `margin-top: ${top}px; height: ${height}px`; // 导航栏内容高度 和 设置的 margin top
}
},
mounted: function() {},
methods: {}
};
export default navigationMixin;
设置dom对应动态高度
依次把定义好的fiexd高度,内容高度进行设置
// BaseNavigation.vue
<view class="navigation-box">
<view class="fixed-navigation">
<view class="navigation" :style="computedNavigationStyle">
<view class="navigation-container" :style="computedNavigationContainerStyle">
<view class="left">
<image class="icon" @click="onBack" :src="`/static/images/public/back.png`"/>
</view>
<view class="center">{{ title }}</view>
<view class="right"></view>
</view>
</view>
</view>
</view>
navigation 总高度
computedNavigationContainerStyle 内容高度
导航栏主题切换
大家都知道导航栏有深色或者浅色主题,这里我们也需要进行设置,深色就用白色返回图片,浅色就用黑色返回图片。
// BaseNavigation.vue
<view class="navigation-box">
<view class="fixed-navigation" :class="type === `white` ? `` : `black-box`">
<view class="navigation" :style="computedNavigationStyle">
<view class="navigation-container" :style="computedNavigationContainerStyle">
<view class="left">
<image class="icon" @click="onBack" ::src="`/static/images/public/${type === `white` ? `l_arrow_w_icon` : `l_arrow_icon`}.png`"/>
</view>
<view class="center">{{ title }}</view>
<view class="right"></view>
</view>
</view>
</view>
</view>
type 通过这个字段进行区分
动态导航栏主题
我们见过很多应用中滚动一个页面时,顶部导航栏会变色,这种情况我们应该怎么样实现呢
// BaseNavigation.vue
computed: {
navigationFixedStyle: function() {
const bg = this.navigationActive ? this.activeBg : this.defaultBg;
return `background: ${bg}`;
},
navigationStyle: function() { // 无效占位高度时设置
const height = this.fixed ? 0.1 : this.navigationHeight;
return `height: ${height}px`;
}
},
通过 navigationActive 来判断是否页面滚动了并随之更新主题色
navigationActive从何而来?
// 定义一个滚动相关的函数
// navigationMixin
onPageScroll({ scrollTop }) {
this.navigationActive = scrollTop > this.scollHeight; // scollHeight = 46
},
定义props和返回函数
props: {
fixed: { type: Boolean, default: false }, // 判定是否需要导航栏高度 沉浸式背景图使用自定义导航时无需高度
title: { type: String, default: "" },
type: { type: String, default: "white" },
defaultBg: { type: String, default: "" }, // #FFFFFF
activeBg: { type: String, default: "" },
navigationActive: { type: Boolean }
},
mixins: [navigationMixin],
methods: {
onBack() {
uni.navigateBack({
delta: 1,
success: (success) => {
console.log("navigateBack success", success);
},
fail: (fail) => {
console.log("navigateBack fail", fail);
uni.switchTab({ url: `/pages/tabBarHome/index` });
}
});
},
}
class style
<style lang="scss" scoped>
.black-box{
.center{
color: #000000 !important;
}
}
.navigation-box{
flex-shrink: 0; // base-layout flex 模式下 高度不被挤压
// height: calc(88rpx + var(--status-bar-height));
background: none;
}
.fixed-navigation{
position: fixed;
top: 0;
left: 0;
width: 100%;
transition: all 200ms;
z-index: 98;
.navigation{
display: flex;
}
.navigation-container{
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.left{
flex: 1;
display: flex;
align-items: center;
.icon{ width: 18rpx; height: 30rpx; padding: 12rpx 32rpx; }
}
.center{
flex: 1;
display: flex;
align-items: center;
font-weight: 700;
font-size: 32rpx;
color: #fff;
justify-content: center;
}
.right{ // 占位
display: flex;
flex: 1;
}
}
}
</style>
实际使用和效果预览
<BaseNavigation
:navigationActive="navigationActive"
title="确认订单"
type="white"
defaultBg="#43A9FE"
activeBg="#6DD0F9"
/>
navigationActive 需要在你用这个组件的页面导入navigationMixin, 否则navigationActive不存在
预览图
滚动图
总结
关于微信上如何自定义导航栏就分享结束了,这个组件还有很多扩展的空间, 比如在没有胶囊按钮的小程序中如何定义,编译成APP后这个导航栏又该如何定义,有需求的小伙伴可自行探索。我这里给出一个小设计思路希望对大家有用。
- 定义一个文件夹BaseNavigation,在下面创建多个场景的导航栏文件,比如 wxNavigation, appNavigation
- 定义一个index.vue; 导入你编写的所有环境的导航栏,通过uniapp的条件编译在dom中分别写好即可
<view class="all-navigation-box">
<!-- #ifdef MP-WEIXIN -->
<WxNavigation props="props"/>
<!-- #endif -->
<!-- #ifdef APP-PLUS -->
<AppNavigation props="props"/>
<!-- #endif -->
</view>