背景
在uniapp中,原生的头部不适合我们,就需要自定义头部满足场景需求。
本文就从0到1封装一个自定义头部,对应的组件已发布插件市场cos-header,可以下载进行体验。
1. 不同平台对自定义头部的表现形式
如何开启自定义头部,在pages.json中将对应页面的style中添加"navigationStyle": "custom"
即可。
不同平台设置后的表现形式也不同,下面主要说明微信、支付宝、h5、app的差别:
- 微信小程序:设置后,胶囊存在,需要考虑胶囊的宽度
- h5、App:顶部没有显示
- 支付宝小程序:进行自定义头部,顶部的导航栏还是存在,如果要设置动态的标题,可调用
uni.setNavigationBarTitle
进行设置。
2. 哪些应用场景
先看几张图片:
-
需要设置字体颜色
-
只需返回按钮
-
需要自定义输入框
-
滚动渐变效果
还可能需要自定义头部背景等等。
3. 具体实现
对于h5和app我们不需要考虑胶囊的大小,支付宝不支持就隐藏,只要说明下微信小程序的实现。
先看一张图:
头部组成主要有: 状态栏 + 导航栏,导航栏分为左边+中间+右边,左右基本设置成一致,那么我们就只需要得到胶囊大小,就能确定布局。
3.1 获取宽高信息
那我们需要获取状态栏高度、胶囊高度信息
getRectInfo() {
// 获取状态栏高度
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight
// 默认高度
this.height = this.statusBarHeight + this.defaultNavBarheight
// #ifndef APP || H5
// 判断获取微信小程序胶囊API是否可用 H5出现为true情况无法使用
if (uni.canIUse('getMenuButtonBoundingClientRect')) {
// 获取微信小程序胶囊布局位置信息
this.menuButtonRect = uni.getMenuButtonBoundingClientRect()
// (胶囊上部高度-状态栏高度)*2 + 胶囊高度 = 导航栏高度(不包含状态栏)
// 以此保证胶囊位于中间位置,多机型适配
this.navBarHeight = (this.menuButtonRect.top - sysInfo.statusBarHeight) * 2 + this.menuButtonRect
.height
// 状态栏高度 + 导航栏高度 = 自定义导航栏高度总和
this.height = this.statusBarHeight + this.navBarHeight
}
// #endif
},
3.2 当前页是否首页
如果当前页面为第一个,展示的返回按钮为主页,不是返回按钮
const pages = getCurrentPages()
this.isFirstPage = pages.length === 1
首次进入页面:
其他页面跳转:
其余这里不做过多说明,请看具体整体代码实现和文档cos-header
3.3 整体代码实现
<template>
<!-- #ifndef MP-ALIPAY -->
<view class="cos-header" :class="{ 'is_fixed': fixed }"
:style="{ height: height + 'px', backgroundColor: backgroundColor, zIndex: zIndex, color: fontColor }">
<image :src="backgroundImage" class="nav-bg" mode="scaleToFill" :style="{ height: height + 'px' }"></image>
<!-- 状态栏占位 -->
<div :style="{ height: statusBarHeight + 'px' }"></div>
<!-- 导航栏 -->
<view class="nav-wrapper" :style="{ height: navBarHeight + 'px' }">
<!-- 返回按钮 -->
<view class="nav-back" v-if="isShowLeft" :style="{ width: menuButtonRect.width + 'px' }" @click="handleBack">
<slot name="left">
<!-- 支付宝自定义还是有返回按钮 -->
<template v-if="isShowBack">
<image v-if="isFirstPage" :src="homeIcon || '../../static/home.svg'" class="img">
</image>
<image v-else :src="backIcon || '../../static/prev.svg'" class="img"></image>
</template>
</slot>
</view>
<view class="nav-title">
<slot>
{{ title }}
</slot>
</view>
<!-- 胶囊位置 -->
<view class="nav-menu" v-if="isShowRight" :style="{ width: menuButtonRect.width + 'px' }">
<slot name="right"></slot>
</view>
</view>
</view>
<!-- #endif -->
</template>
<script>
/**
* NavBar 自定义导航栏
* @description 导航栏组件,主要用于头部导航
* @property {String} title 标题文字
* @property {String} homeUrl 左侧点击返回主页地址
* @property {String} homeIcon 左侧主页图标
* @property {String} backUrl 左侧点击返回地址
* @property {String} backIcon 左侧返回图标
* @property {Boolean} fixed = [true|false] 是否固定顶部
* @property {Number} zIndex = 显示层级
* @property {String} backgroundColor 导航栏背景颜色
* @property {String} backgroundImage 导航栏背景图片
* @property {String} fontColor 图标和文字颜色
* @property {Boolean} isShowBack 是否显示返回图标
* @property {Boolean} isShowLeft 是否显示左侧
* @property {Boolean} isShowRight 是否显示右侧
* @property {Number} defaultNavBarheight 默认导航栏高度
* @property {Number} defaultMenuWidth 默认菜单宽度
* @property {Object} jumpMap 主页跳转方法映射
*/
export default {
name: "CosHeader",
props: {
title: {
type: String,
default: ''
},
homeUrl: {
type: String,
default: '/pages/index/index'
},
homeIcon: {
type: String,
default: ''
},
backUrl: {
type: String,
default: ''
},
backIcon: {
type: String,
default: ''
},
fixed: {
type: Boolean,
default: false
},
zIndex: {
type: Number,
default: 99
},
backgroundColor: {
type: String,
default: '#fff'
},
backgroundImage: {
type: String,
default: ''
},
fontColor: {
type: String,
default: '#000'
},
isShowBack: {
type: Boolean,
default: true
},
isShowLeft: {
type: Boolean,
default: true
},
isShowRight: {
type: Boolean,
default: true
},
defaultNavBarheight: {
type: Number,
default: 40
},
defaultMenuWidth: {
type: Number,
default: 40
},
jumpMap: {
type: Object,
default: () => ({
home: '',
back: ''
})
}
},
data() {
return {
// 状态栏
statusBarHeight: 0,
// 默认导航栏高度设置40,针对app和h5情况
navBarHeight: this.defaultNavBarheight,
// 胶囊信息
menuButtonRect: {
width: this.defaultMenuWidth
},
height: this.defaultNavBarheight,
isFirstPage: true,
}
},
mounted() {
this.init()
},
methods: {
init() {
this.getRectInfo()
this.getPageInfo()
this.setTitle()
},
setTitle() {
// 针对支付宝进行动态设置
// #ifdef MP-ALIPAY
uni.setNavigationBarTitle({
title: this.title
});
// #endif
// #ifdef H5
document.title = this.title
// #endif
},
getRectInfo() {
// 获取状态栏高度
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight
// 默认高度
this.height = this.statusBarHeight + this.defaultNavBarheight
// #ifndef APP || H5
// 判断获取微信小程序胶囊API是否可用 H5出现为true情况无法使用
if (uni.canIUse('getMenuButtonBoundingClientRect')) {
// 获取微信小程序胶囊布局位置信息
this.menuButtonRect = uni.getMenuButtonBoundingClientRect()
// (胶囊上部高度-状态栏高度)*2 + 胶囊高度 = 导航栏高度(不包含状态栏)
// 以此保证胶囊位于中间位置,多机型适配
this.navBarHeight = (this.menuButtonRect.top - sysInfo.statusBarHeight) * 2 + this.menuButtonRect
.height
// 状态栏高度 + 导航栏高度 = 自定义导航栏高度总和
this.height = this.statusBarHeight + this.navBarHeight
}
// #endif
},
getPageInfo() {
const pages = getCurrentPages()
this.isFirstPage = pages.length === 1
},
handleBack() {
if (!this.isShowBack) return
if (!this.isFirstPage && !this.backUrl) {
uni.navigateBack({
delta: 1
})
return
}
const url = this.isFirstPage ? this.homeUrl : this.backUrl
if (!url) return
const type = this.isFirstPage ? (this.jumpMap.home || '') : (this.jumpMap.back || '')
if (type) {
uni[type]({
url
})
return
}
try {
uni.navigateTo({
url
})
} catch (error) {
uni.switchTab({
url
})
}
}
}
}
</script>
<style>
.cos-header {
position: relative;
width: 100vw;
}
.nav-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
width: 100vw;
}
.is_fixed {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 99;
}
.nav-wrapper {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8px;
}
.img {
width: 20px;
height: 20px;
color: #fff;
}
.nav-back {
flex-shrink: 0;
}
.nav-title {
text-align: center;
white-space: nowrap;
overflow: hidden;
word-break: break-all;
text-overflow: ellipsis;
}
.nav-menu {
flex-shrink: 0;
}
</style>
3.4 如何实现滚动渐变
滚动渐变本质上就是修改背景色和字体颜色,可以配置uniapp中的onPageScroll
轻松实现。
代码如下:
<template>
<view class="content">
<cos-header title="导航栏组件" :background-color="backgroundColor" :font-color="fontColor" :fixed="fixed">
</cos-header>
<image src="/static/sync_bg.jpg" mode="" style="width: 100vw;"></image>
<div v-for="item in 100" :key="item">
{{ item }}
</div>
</view>
</template>
<script>
export default {
data() {
return {
backgroundColor: '',
fontColor: '#fff',
fixed: true,
}
},
onPageScroll(e) {
if (e.scrollTop < 5) {
this.fontColor = '#fff'
this.backgroundColor = ''
} else if (e.scrollTop < 50) {
const rate = e.scrollTop / 50
this.backgroundColor = `rgba(255,255,255, ${rate})`
this.fontColor = `rgba(0,0,0, ${rate})`
} else {
this.backgroundColor = 'rgba(255,255,255)'
this.fontColor = 'rgba(0,0,0)'
}
}
}
</script>
4. 效果展示
具体文档请查看cos-header
体验预览效果:
5. 总结
通过这次封装,主要学习不同平台的表现,学习对应的应用场景。希望能对你有所帮助,如有错误,请指正O^O!