2023-11-14 17:21:03 +08:00
|
|
|
|
<!--
|
|
|
|
|
@name: index
|
|
|
|
|
@author: kahu4
|
|
|
|
|
@date: 2023-11-09 10:56
|
|
|
|
|
@description:index
|
|
|
|
|
@update: 2023-11-09 10:56
|
|
|
|
|
-->
|
|
|
|
|
<script setup>
|
|
|
|
|
import { computed, defineProps, ref, toRefs, unref, watch } from "vue";
|
|
|
|
|
import { onLoad } from "@dcloudio/uni-app";
|
|
|
|
|
import { useRouter } from "@/hooks/useRouter";
|
|
|
|
|
import { createAnimation } from "@/utils/utils";
|
2023-11-15 19:59:37 +08:00
|
|
|
|
import { useScroll } from "@/hooks/useScroll";
|
2023-11-14 17:21:03 +08:00
|
|
|
|
|
|
|
|
|
const HEADER_HEIGHT = 40 // header高度
|
|
|
|
|
|
|
|
|
|
const {goBack} = useRouter()
|
|
|
|
|
/**
|
|
|
|
|
* @property {String} systemBarAreaBg 系统导航条区域背景颜色
|
|
|
|
|
* @property {String} headerAreaBg header区域背景颜色
|
|
|
|
|
* @property {String} headerAreaTextColor header区域字体
|
|
|
|
|
* @property {Boolean} showReturn 是否展示返回按钮
|
|
|
|
|
* @property {String} returnColor 返回按钮的颜色
|
|
|
|
|
* @property {Number} returnSize 返回按钮的大小
|
|
|
|
|
* @property {Boolean||String} textShadow 字体阴影
|
|
|
|
|
* @property {Boolean} bgChangeByScroll 是否随着页面滚动增加header背景(包括system-bar-area)
|
|
|
|
|
* @property {String} bgChangeColor 滚动时候变换的颜色
|
|
|
|
|
* @property {Number} scrollTop 当前页面高度
|
|
|
|
|
* @property {Boolean} showRight 是否需要右边
|
|
|
|
|
* @property {number} leftWidth 左侧(返回)宽度(设置了此属性title将不再居中)
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
systemBarAreaBg: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: () => '#FFFFFF00' // 透明 #FFFFFF00
|
|
|
|
|
},
|
|
|
|
|
headerAreaBg: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: () => '#FFFFFF00' // 透明 #FFFFFF00
|
|
|
|
|
},
|
|
|
|
|
headerAreaTextColor: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: () => '#000000'
|
|
|
|
|
},
|
|
|
|
|
showReturn: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: () => true
|
|
|
|
|
},
|
|
|
|
|
returnColor: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: () => '#000'
|
|
|
|
|
},
|
|
|
|
|
returnSize: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: () => 22
|
|
|
|
|
},
|
|
|
|
|
textShadow: {
|
|
|
|
|
type: [Boolean, String],
|
|
|
|
|
default: () => false
|
|
|
|
|
},
|
|
|
|
|
bgChangeByScroll: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: () => true
|
|
|
|
|
},
|
|
|
|
|
bgChangeColor: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: () => '#fff'
|
|
|
|
|
},
|
|
|
|
|
propUp: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: () => true
|
|
|
|
|
},
|
|
|
|
|
showRight: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: () => true
|
|
|
|
|
},
|
|
|
|
|
leftWidth: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: () => 0
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
systemBarAreaBg,
|
|
|
|
|
headerAreaBg,
|
|
|
|
|
headerAreaTextColor,
|
|
|
|
|
showReturn,
|
|
|
|
|
returnColor,
|
|
|
|
|
returnSize,
|
|
|
|
|
textShadow,
|
|
|
|
|
bgChangeByScroll,
|
|
|
|
|
bgChangeColor,
|
|
|
|
|
propUp,
|
|
|
|
|
showRight,
|
|
|
|
|
leftWidth
|
|
|
|
|
} = toRefs(props)
|
|
|
|
|
|
|
|
|
|
const emits = defineEmits(['getSystemInfo', 'animation'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 高度信息 单位px
|
|
|
|
|
const heightInfo = ref({
|
|
|
|
|
safeAreaInsets: {bottom: 0, top: 0, left: 0, right: 0}, // 安全区信息
|
|
|
|
|
statusBarHeight: 0, // 状态栏高度
|
|
|
|
|
screenWidth: 0, // 屏幕内部宽度
|
|
|
|
|
screenHeight: 0, // 屏幕内部高度
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取系统信息,设置导航条
|
|
|
|
|
*/
|
|
|
|
|
function getSystemInfo() {
|
|
|
|
|
const res = uni.getSystemInfoSync();
|
|
|
|
|
const heightObj = unref(heightInfo)
|
|
|
|
|
heightObj.safeAreaInsets = res.safeAreaInsets
|
|
|
|
|
heightObj.statusBarHeight = res.statusBarHeight
|
|
|
|
|
heightObj.screenWidth = res.screenWidth || res.windowWidth
|
|
|
|
|
heightObj.screenHeight = res.screenHeight || res.windowHeight
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 胶囊信息 单位px
|
|
|
|
|
const menuInfo = ref({
|
|
|
|
|
bottom: 0,
|
|
|
|
|
height: 0,
|
|
|
|
|
left: 0,
|
|
|
|
|
right: 0,
|
|
|
|
|
top: 0,
|
|
|
|
|
width: 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取小程序右上角信息
|
|
|
|
|
*/
|
|
|
|
|
function getMenuInfo() {
|
|
|
|
|
const menuButtonBoundingClientRect = uni.getMenuButtonBoundingClientRect();
|
|
|
|
|
if (menuButtonBoundingClientRect) {
|
|
|
|
|
menuInfo.value = {...menuButtonBoundingClientRect}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// scss全局变量
|
|
|
|
|
const scssVarStyle = computed(() => {
|
|
|
|
|
return {
|
2023-11-15 19:59:37 +08:00
|
|
|
|
'--header-height': `${ HEADER_HEIGHT }px`
|
2023-11-14 17:21:03 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 系统导航条区域样式
|
|
|
|
|
const systemBarAreaStyle = computed(() => {
|
|
|
|
|
return {
|
|
|
|
|
width: '100%',
|
2023-11-15 19:59:37 +08:00
|
|
|
|
height: `${ unref(heightInfo).statusBarHeight * 2 }rpx`,
|
2023-11-14 17:21:03 +08:00
|
|
|
|
background: unref(systemBarAreaBg)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// header区域样式
|
|
|
|
|
const headerAreaStyle = computed(() => {
|
|
|
|
|
// 计算margin top
|
|
|
|
|
// margin-top (导航条高度 - 胶囊高度) / 2 永远确保胶囊在header中央
|
2023-11-15 19:59:37 +08:00
|
|
|
|
const marginTop = unref(menuInfo).height > 0 ? `-${((HEADER_HEIGHT - unref(menuInfo).height))/2}px` : 0
|
2023-11-14 17:21:03 +08:00
|
|
|
|
return {
|
|
|
|
|
width: '100%',
|
|
|
|
|
background: unref(headerAreaBg),
|
|
|
|
|
color: unref(headerAreaTextColor),
|
|
|
|
|
marginTop
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 文本样式
|
|
|
|
|
const textShadowStyle = computed(() => {
|
|
|
|
|
return {
|
|
|
|
|
textShadow: unref(textShadow) ? unref(textShadow) : 'none',
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const titleStyle = computed(() => {
|
|
|
|
|
let width = unref(leftWidth) <= 0 ? '' : `calc( 100vw - var(--side-distance) - var(--side-distance) - ${ leftWidth.value }rpx )`
|
|
|
|
|
// #ifdef MP-WEIXIN
|
|
|
|
|
width = unref(leftWidth) <= 0 ? '' : `calc( 100vw - var(--side-distance) - var(--side-distance) - ${ leftWidth.value }rpx - ${ menuInfo.value.width }px )`
|
|
|
|
|
// #endif
|
|
|
|
|
return {
|
|
|
|
|
width,
|
|
|
|
|
left: unref(leftWidth) <= 0 ? '50%' : `calc( var(--side-distance) + ${ leftWidth.value }rpx )`,
|
|
|
|
|
textShadow: unref(textShadow) ? unref(textShadow) : 'none',
|
|
|
|
|
transform: unref(leftWidth) <= 0 ? 'translateX(-50%) translateY(-50%)' : 'translateX(0) translateY(-50%)'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 滚动后背景样式
|
|
|
|
|
const scrollMaskStyle = computed(() => {
|
|
|
|
|
return {
|
|
|
|
|
background: unref(bgChangeColor),
|
|
|
|
|
opacity: unref(scrollTransparency)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 总高度
|
|
|
|
|
const containerHeight = computed(() => {
|
2023-11-15 19:59:37 +08:00
|
|
|
|
return (unref(heightInfo).statusBarHeight + HEADER_HEIGHT)
|
2023-11-14 17:21:03 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
let animation
|
|
|
|
|
const scrollTransparency = ref(0) // 滚动透明度
|
|
|
|
|
function doCreateAnimation() {
|
|
|
|
|
const scrollEnd = heightInfo.value.safeAreaInsets.bottom + HEADER_HEIGHT + 100
|
|
|
|
|
animation = createAnimation(0, scrollEnd, 0, 1)
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-15 19:59:37 +08:00
|
|
|
|
const {scrollTop} = useScroll();
|
2023-11-14 17:21:03 +08:00
|
|
|
|
watch(scrollTop, () => {
|
|
|
|
|
if (!bgChangeByScroll.value) return
|
|
|
|
|
if (!animation) doCreateAnimation()
|
|
|
|
|
scrollTransparency.value = animation(unref(scrollTop));
|
|
|
|
|
emits('animation', scrollTransparency.value)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defineExpose({containerHeight, heightInfo})
|
|
|
|
|
|
|
|
|
|
onLoad(() => {
|
|
|
|
|
getSystemInfo()
|
|
|
|
|
doCreateAnimation()
|
|
|
|
|
// #ifdef MP-WEIXIN
|
|
|
|
|
getMenuInfo()
|
|
|
|
|
// #endif
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<view
|
|
|
|
|
class="container"
|
|
|
|
|
:style="scssVarStyle"
|
|
|
|
|
>
|
|
|
|
|
<view class="header-container">
|
|
|
|
|
<!-- 头部系统导航条区域 -->
|
|
|
|
|
<view
|
|
|
|
|
class="system-bar-area"
|
|
|
|
|
:style="systemBarAreaStyle"
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
</view>
|
|
|
|
|
<!-- header -->
|
|
|
|
|
<view
|
|
|
|
|
class="header-row"
|
|
|
|
|
:style="headerAreaStyle"
|
|
|
|
|
>
|
|
|
|
|
<view
|
|
|
|
|
class="left"
|
|
|
|
|
:style="textShadowStyle"
|
|
|
|
|
v-if="showReturn"
|
|
|
|
|
>
|
|
|
|
|
<slot name="left">
|
|
|
|
|
<uv-icon
|
|
|
|
|
name="arrow-left"
|
|
|
|
|
:color="returnColor"
|
|
|
|
|
:size="returnSize"
|
|
|
|
|
@click="goBack"
|
|
|
|
|
/>
|
|
|
|
|
</slot>
|
|
|
|
|
</view>
|
|
|
|
|
<view
|
|
|
|
|
class="title"
|
|
|
|
|
:style="titleStyle"
|
|
|
|
|
>
|
|
|
|
|
<slot>
|
|
|
|
|
</slot>
|
|
|
|
|
</view>
|
|
|
|
|
<view
|
|
|
|
|
class="right"
|
|
|
|
|
v-if="showRight"
|
|
|
|
|
>
|
|
|
|
|
<slot name="right">
|
|
|
|
|
</slot>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 背景 mask -->
|
|
|
|
|
<view
|
|
|
|
|
class="bg-mask"
|
|
|
|
|
:style="scrollMaskStyle"
|
|
|
|
|
></view>
|
|
|
|
|
|
|
|
|
|
</view>
|
|
|
|
|
<!-- 撑起 -->
|
|
|
|
|
<view
|
|
|
|
|
class="prop-up"
|
2023-11-15 19:59:37 +08:00
|
|
|
|
:style="{height:`${containerHeight}px`}"
|
2023-11-14 17:21:03 +08:00
|
|
|
|
v-if="propUp"
|
|
|
|
|
></view>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style
|
|
|
|
|
scoped
|
|
|
|
|
lang="scss"
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
.container {
|
|
|
|
|
--header-height: 0rpx;
|
|
|
|
|
--side-distance: 30rpx;
|
|
|
|
|
$side-distance: var(--side-distance); // 侧边距
|
|
|
|
|
$header-height: var(--header-height); // header高度
|
|
|
|
|
.header-container {
|
|
|
|
|
width: 100%;
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
z-index: 99;
|
|
|
|
|
|
|
|
|
|
.system-bar-area {
|
|
|
|
|
width: 100%;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-row {
|
|
|
|
|
z-index: 1;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: $header-height;
|
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
|
|
.left, .right, .title {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 50%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.left {
|
|
|
|
|
left: $side-distance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.right {
|
|
|
|
|
right: $side-distance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.title {
|
|
|
|
|
left: 50%;
|
|
|
|
|
transform: translateX(-50%) translateY(-50%);
|
|
|
|
|
color: #333;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bg-mask {
|
|
|
|
|
z-index: 0;
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
box-shadow: 0 0 15rpx rgba(162, 162, 162, 0.5);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.prop-up {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: $header-height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
</style>
|