封装原因
项目中有时候需要使用自定义的头部导航栏,原生的无法满足需求
参数
| 属性名 | 描述 | 示例 |
|---|---|---|
| title | 标题 | 字符串:'首页' |
| titleStyle | 标题的style样式 | 对象:{} |
| navbarStyle | 导航栏的style样式 | 对象:{} |
| bgColor | 导航栏的背景色 | 字符串:'#fff' |
| type | 左侧胶囊的内容 | 字符串:'all',详细值请在下方查看 |
| isPlaceholder | 是否占位 | Boolean: true |
| zIndex | 导航栏的层级 | Number |
参数解释
type
- 用于控制左侧胶囊的内容
- all 有返回和回到首页
- back 只有返回
- home 只有回到首页
- none 什么都没有
注意点
- 组件提供3个插槽:left、title、right
- 在小程序端right插槽不生效(一般来说,因为小程序右侧胶囊的存在,也不会有设计在右侧的内容)
- 如果使用了left插槽,那么参数type会失效
- 如果使用了title插槽,那么参数title会失效
代码
<template>
<view
class="navbar-box"
:style="{ ...props.navbarStyle, 'background-color': props.bgColor, 'z-index': zIndex }"
v-if="placeholderHeight"
>
<!-- 状态栏 -->
<view :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 导航栏 -->
<view :style="{ height: navBarHeight + 'px' }">
<div class="navbar-default">
<div class="left-box">
<slot name="left">
<div
class="default-left"
v-if="props.type != 'none'"
>
<view
class="default-all"
v-if="props.type == 'all'"
>
<text @click="goBack">返回</text>
<view class="default-line"></view>
<text @click="goHome">首页</text>
</view>
<view
class="default-back"
v-if="props.type == 'back'"
>
<text @click="goBack">返回</text>
</view>
<view
class="default-home"
v-if="props.type == 'home'"
>
<text @click="goHome">首页</text>
</view>
</div>
</slot>
</div>
<slot name="title">
<view
class="default-title"
:style="props.titleStyle"
>{{ props.title }}</view
>
</slot>
<!-- #ifndef MP -->
<div class="right-box">
<slot name="right"> </slot>
</div>
<!-- #endif -->
</div>
</view>
</view>
<!-- 占位 -->
<view
:style="{ height: placeholderHeight + 'px' }"
v-if="props.isPlaceholder"
></view>
</template>
<script setup>
import { ref, onMounted, computed, watchEffect } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
const props = defineProps({
title: {
type: String,
default: '',
},
titleStyle: {
type: Object,
},
navbarStyle: {
type: Object,
},
bgColor: {
type: String,
default: '#fff',
},
type: {
type: String,
default: 'all',
},
isPlaceholder: {
type: Boolean,
default: true,
},
zIndex: {
type: Number,
default: 2000,
},
});
const emit = defineEmits(['getNavbarHeight']);
onMounted(() => {
geStatusBarHeight();
getNavBarHeight();
emit('getNavbarHeight', statusBarHeight.value + navBarHeight.value);
});
let statusBarHeight = ref(0);
let navBarHeight = ref(0);
// 小程序右侧胶囊按钮信息
let menuButtonInfo = ref('');
// 计算占位盒子高度
const placeholderHeight = computed(() => {
return statusBarHeight.value + navBarHeight.value;
});
// 获取状态栏高度
function geStatusBarHeight() {
// #ifdef H5
statusBarHeight.value = 0;
// #endif
// #ifdef MP-WEIXIN || APP-PLUS
statusBarHeight.value = uni.getWindowInfo().statusBarHeight;
// #endif
}
// 获取导航栏高度
function getNavBarHeight() {
// #ifdef MP-WEIXIN
menuButtonInfo.value = uni.getMenuButtonBoundingClientRect();
navBarHeight.value =
menuButtonInfo.value.height +
(menuButtonInfo.value.top - uni.getWindowInfo().statusBarHeight) * 2 +
2;
// #endif
// #ifdef APP-PLUS
if (uni.getDeviceInfo().osName == 'android') {
navBarHeight.value = 48;
} else {
navBarHeight.value = 44;
}
// #endif
// #ifdef H5
navBarHeight.value = 44;
// #endif
}
// 返回上一页
function goBack() {
const pages = getCurrentPages();
if (pages.length == 1) {
uni.reLaunch({
url: '/pages/home/index',
});
return;
}
uni.navigateBack();
}
// 去首页
function goHome() {
uni.reLaunch({
url: '/pages/home/index',
});
}
</script>
<style lang="scss" scoped>
.navbar-box {
position: fixed;
top: 0;
left: 0;
width: 100%;
}
.navbar-default {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.left-box {
position: absolute;
left: 20rpx;
top: 50%;
transform: translateY(-50%);
}
.default-left {
.default-all {
display: flex;
justify-content: center;
align-items: center;
padding: 10rpx 30rpx;
background: rgba(0, 0, 0, 0.5);
border-radius: 32rpx;
.default-line {
width: 2rpx;
height: 40rpx;
background: #afafaf;
margin: 0 20rpx;
}
color: #fff;
}
.default-back,
.default-home {
width: 63rpx;
height: 63rpx;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
}
.default-title {
color: #000;
font-weight: bold;
font-size: 16px;
}
.right-box {
position: absolute;
right: 20rpx;
top: 50%;
transform: translateY(-50%);
}
</style>
页面使用
<myNavbar title="标题" @getNavbarHeight="getNavbarHeight">
<template #left> 左侧内容 </template>
<template #title> 标题内容 </template>
<template #right> 右侧内容 </template>
</myNavbar>
function getNavbarHeight(height) {
console.log(height);
}
监听页面滚动距离
:bgColor="scrollTop > 20 ? '#D2F5DA' : 'transparent'"
// 监听页面滚动距离
const scrollTop = ref(0);
onPageScroll((e) => {
scrollTop.value = e.scrollTop;
});