426 lines
11 KiB
Plaintext
426 lines
11 KiB
Plaintext
<script lang="ts" setup>
|
|
|
|
import { PropType, onMounted } from "vue"
|
|
import { getUid } from "../../core/util/xCoreUtil.uts"
|
|
import { getDefaultColor, setBgColorLightByDark, isBlackAndWhite, hexToRgb } from "../../core/util/xCoreColorUtil.uts"
|
|
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
|
|
import { xConfig } from "../../config/xConfig.uts"
|
|
import { xProvitae } from "../../config/xConfig.uts"
|
|
/**
|
|
* @name 标题导航 xNavbar
|
|
* @description 标题导航,用途页面头部标题。可以默认透明,滚动实现背景可变的标题导航。
|
|
* @page /pages/index/navbar
|
|
* @category 导航组件
|
|
* @constant 平台兼容
|
|
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
|
|
*/
|
|
defineOptions({name:"xNavbar"})
|
|
const emits = defineEmits([
|
|
/**
|
|
* 当滚动页面时,导航栏位置
|
|
* 与滚动之间的距离差比(0~1)
|
|
* 0表示在顶部,1表示已经超过了导航高
|
|
* 主要是用来类型滚动时设置导航顶部的一些布局变化。
|
|
*/
|
|
'fiexdChange',
|
|
/**
|
|
* 初始化完成向下发送一个事件用于告知本组件实际的高
|
|
* @param {number} height -单位px,组件高。
|
|
*/
|
|
'init'
|
|
])
|
|
const props = defineProps(
|
|
{
|
|
/**
|
|
* 是否底部占位内容,如果为false底部悬空。你的页面内容将自动压在导航栏的底部。
|
|
*/
|
|
isPlace: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
/**
|
|
* 背景颜色,注意这是静态时的背景色。
|
|
*/
|
|
bgColor: {
|
|
type: String,
|
|
default: 'white'
|
|
},
|
|
/**
|
|
* 暗黑的背景颜色。
|
|
*/
|
|
darkBgColor: {
|
|
type: String,
|
|
default: "#000000"
|
|
},
|
|
/**
|
|
* 背景颜色,这是滑动时超过指定本状态栏高度时自动渐变到此颜色
|
|
* 如果为空时,不会有动态背景
|
|
* 如果提供的是白或者黑,暗黑时自动取反。
|
|
*/
|
|
activeBgColor: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
/**
|
|
* 返回按钮的颜色.默认是取titleColor,如果你单独定义了
|
|
* 以你定义的为准
|
|
*/
|
|
backColor: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
/**
|
|
* 标题
|
|
*/
|
|
title: {
|
|
type: String,
|
|
default: '标题'
|
|
},
|
|
/**
|
|
* 默认标题颜色,暗黑是取白,如果有其它需求建议插槽。
|
|
*/
|
|
titleColor: {
|
|
type: String,
|
|
default: '#333333'
|
|
},
|
|
/**
|
|
* 动态悬浮时标题颜色,如果为暗黑时,你提供的颜色为白或者黑会反色
|
|
* 如果提供的是彩色自动加深或者提亮
|
|
*/
|
|
titleActiveColor: {
|
|
type: String,
|
|
default: '#333333'
|
|
},
|
|
|
|
/**
|
|
* 标题文字大小
|
|
*/
|
|
titleFontSize: {
|
|
type: String,
|
|
default: '17',
|
|
},
|
|
/**
|
|
* 右边的宽度。
|
|
*/
|
|
lrWidth: {
|
|
type: String,
|
|
default: '100',
|
|
},
|
|
/**
|
|
* 左边的宽度。
|
|
*/
|
|
llWidth: {
|
|
type: String,
|
|
default: '100',
|
|
},
|
|
/**
|
|
* 层级。
|
|
*/
|
|
zIndex: {
|
|
type: Number,
|
|
default: 90,
|
|
},
|
|
/**
|
|
* 左边按钮默认点击返回上页。
|
|
* 但如果上页返回失败(通常见于直接程序启动本面,无法进行上页返回时)
|
|
* 失败后返回的页面。默认是首页。
|
|
*/
|
|
backErrorPath: {
|
|
type: String,
|
|
default: '/pages/index/index'
|
|
},
|
|
/**
|
|
* 是否显示返回按钮。
|
|
*/
|
|
showNavBack: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
/**
|
|
* 渐变背景,如果提供,上面的BgColor背景和暗黑背景将失效。
|
|
* 例:['to right','#ff667f','#ff5416']
|
|
*/
|
|
linearGradient: {
|
|
type: Array as PropType<string[]>,
|
|
default: () : string[] => [] as string[]
|
|
},
|
|
/**
|
|
* 动态悬浮时的渐变背景,
|
|
* 提供后上面的 activeBgColor的背景和暗黑背景将失效。
|
|
* 例:['to right','#ff667f','#ff5416']
|
|
*/
|
|
linearActiveGradient: {
|
|
type: Array as PropType<string[]>,
|
|
default: () : string[] => [] as string[]
|
|
},
|
|
/**
|
|
* 静态在顶部时是否透明背景
|
|
*/
|
|
staticTransparent: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
/**
|
|
* 某些时候你可能需要设定最大宽
|
|
* 任意单位;设置为none时表示不设置.
|
|
* 仅web生效
|
|
*/
|
|
maxWidth:{
|
|
type:String,
|
|
default:'none'
|
|
},
|
|
/**
|
|
* 这个高不是最终高,它只是导航高
|
|
* 状态栏高是自动的不包含在这里面.
|
|
* 单位为像素单位,不是rpx
|
|
*/
|
|
height:{
|
|
type:Number,
|
|
default: 50
|
|
}
|
|
|
|
}
|
|
)
|
|
const statusBarHeight = ref(44)
|
|
const navbarHeight = computed(():number=>props.height)
|
|
const isFiexd = ref(false)
|
|
const fiexRatio = ref(1)
|
|
const _stop = ref(0)
|
|
|
|
const _linearGradient = computed(() : string => {
|
|
if (props.linearGradient.length < 3) return '';
|
|
let str = props.linearGradient.join(",");
|
|
return `linear-gradient(${str})`
|
|
})
|
|
const _linearActiveGradient = computed(() : string => {
|
|
if (props.linearActiveGradient.length < 3) return '';
|
|
let str = props.linearActiveGradient.join(",");
|
|
return `linear-gradient(${str})`
|
|
})
|
|
const _lrWidth = computed(() : string => checkIsCssUnit(props.lrWidth, xConfig.unit))
|
|
const _llWidth = computed(() : string => checkIsCssUnit(props.llWidth, xConfig.unit))
|
|
const _titleFontSize = computed(() : string => checkIsCssUnit(props.titleFontSize, xConfig.unit))
|
|
const _bgColor = computed(() : string => {
|
|
let bgcolor = props.bgColor
|
|
if (xConfig.dark == 'dark') {
|
|
bgcolor = props.darkBgColor == "" ? "#000000" : props.darkBgColor
|
|
}
|
|
return getDefaultColor(bgcolor)
|
|
})
|
|
const _maxWidth = computed(():string=>{
|
|
let w = 'none'
|
|
if(props.maxWidth!=''&&props.maxWidth=='none'){
|
|
w = props.maxWidth
|
|
}
|
|
// #ifndef WEB
|
|
w = 'none'
|
|
// #endif
|
|
return w=='none'? w : checkIsCssUnit(props.maxWidth,xConfig.unit)
|
|
})
|
|
const _activeBgColor = computed(() : string => {
|
|
let color = _bgColor.value
|
|
if (props.activeBgColor != "") {
|
|
color = getDefaultColor(props.activeBgColor)
|
|
}
|
|
let isba = isBlackAndWhite(props.titleActiveColor)
|
|
if (isba) {
|
|
if (xConfig.dark == 'dark') {
|
|
color = setBgColorLightByDark(color)
|
|
}
|
|
}
|
|
|
|
return color
|
|
})
|
|
const _titleColor = computed(() : string => {
|
|
if (xConfig.dark == 'dark') return "#ffffff"
|
|
return getDefaultColor(props.titleColor)
|
|
})
|
|
const _backColor = computed(() : string => {
|
|
if (props.backColor != '') return getDefaultColor(props.backColor)
|
|
return _titleColor.value
|
|
})
|
|
const _titleActiveColor = computed(() : string => {
|
|
let isba = isBlackAndWhite(props.titleActiveColor)
|
|
if (isba) {
|
|
if (xConfig.dark == 'dark') return "#ffffff"
|
|
}
|
|
return getDefaultColor(props.titleActiveColor)
|
|
})
|
|
const _title = computed(() : string => props.title)
|
|
const _styleMap = computed(() : Map<string, string> => {
|
|
let maps = new Map<string, string>();
|
|
if (props.staticTransparent) {
|
|
maps.set('opacity', fiexRatio.value.toString());
|
|
} else {
|
|
maps.set('opacity', '1');
|
|
}
|
|
maps.set('height', (statusBarHeight.value + navbarHeight.value) + 'px');
|
|
|
|
if (isFiexd.value) {
|
|
if (_linearActiveGradient.value != '') {
|
|
maps.set('background-image', _linearActiveGradient.value)
|
|
} else if (_activeBgColor.value != '') {
|
|
maps.set('background-color', _activeBgColor.value)
|
|
} else {
|
|
maps.set('background-color', _bgColor.value)
|
|
}
|
|
} else {
|
|
if (_linearGradient.value != '') {
|
|
maps.set('background-image', _linearGradient.value)
|
|
} else {
|
|
|
|
maps.set('background-color', _bgColor.value)
|
|
}
|
|
}
|
|
|
|
return maps
|
|
})
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
let sys = uni.getWindowInfo();
|
|
statusBarHeight.value = sys.statusBarHeight
|
|
emits('init', statusBarHeight.value + navbarHeight.value)
|
|
if (props.staticTransparent) {
|
|
fiexRatio.value = 0
|
|
}
|
|
})
|
|
onPageScroll((evt : OnPageScrollOptions) => {
|
|
_stop.value = evt.scrollTop;
|
|
})
|
|
watchEffect(() => {
|
|
let barheight = statusBarHeight.value + navbarHeight.value;
|
|
isFiexd.value = _stop.value > 0
|
|
if (props.staticTransparent) {
|
|
fiexRatio.value = Math.max(Math.min(_stop.value / barheight, 1), 0)
|
|
}
|
|
emits("fiexdChange", fiexRatio.value)
|
|
})
|
|
const backGo = () => {
|
|
// #ifdef WEB
|
|
window.history.back()
|
|
// #endif
|
|
// #ifndef WEB
|
|
uni.navigateBack({
|
|
fail() {
|
|
uni.reLaunch({
|
|
url: props.backErrorPath
|
|
})
|
|
}
|
|
})
|
|
// #endif
|
|
}
|
|
</script>
|
|
<template>
|
|
<view>
|
|
<view class="xNavbar" :style="{zIndex:zIndex,maxWidth:_maxWidth}">
|
|
|
|
<view :key="_linearActiveGradient" :style="_styleMap" class="xNavbarBg"></view>
|
|
<view class="xNavbarContentBox">
|
|
<view :style="{height:(statusBarHeight)+'px'}"></view>
|
|
<view class="xNavbarContent" :style="{height:(navbarHeight)+'px'}">
|
|
<view class="xNavBarLeft" :style="{width:_llWidth}">
|
|
<view v-if="showNavBack" @click="backGo" class="xNavBarBack" style="paddingLeft: 6px;">
|
|
<x-icon font-size="28" name="arrow-left-s-line"
|
|
:color="isFiexd?_titleActiveColor:_backColor"></x-icon>
|
|
</view>
|
|
<!--
|
|
@slot 左边插槽
|
|
@prop {boolean} isFiexd - 当前是处于悬浮状态还是静态顶部
|
|
-->
|
|
<slot name="left" :isFiexd="isFiexd"></slot>
|
|
</view>
|
|
<view class="xNavBarTtitle">
|
|
<!--
|
|
@slot 标题插槽
|
|
@prop {boolean} isFiexd - 当前是处于悬浮状态还是静态顶部
|
|
-->
|
|
<slot name="title" :isFiexd="isFiexd">
|
|
<text class="xNvbarTtitleOverls" :style="{fontSize:_titleFontSize,fontWeight:'bold',color:isFiexd?_titleActiveColor:_titleColor}">{{_title}}</text>
|
|
</slot>
|
|
</view>
|
|
<view class="xNavBarRight" :style="{width:_lrWidth}">
|
|
<!--
|
|
@slot 右边插槽
|
|
@prop {boolean} isFiexd - 当前是处于悬浮状态还是静态顶部
|
|
-->
|
|
<slot name="right" :isFiexd="isFiexd"></slot>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view v-if="isPlace" :style="{height:(statusBarHeight+navbarHeight)+'px'}"></view>
|
|
</view>
|
|
</template>
|
|
<style scoped>
|
|
.xNvbarTtitleOverls{
|
|
/* #ifdef APP */
|
|
lines:1;
|
|
text-overflow: ellipsis;
|
|
/* #endif */
|
|
/* #ifndef APP */
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 1;
|
|
-webkit-box-orient: vertical;
|
|
width:100%;
|
|
/* #endif */
|
|
text-align: center;
|
|
}
|
|
.xNavbarContentBox {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
}
|
|
|
|
.xNavBarBack {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
/* background-image: linear-gradient; */
|
|
}
|
|
|
|
.xNavbar {
|
|
/* transition-duration: 500ms;
|
|
transition-property: background-color;
|
|
transition-timing-function: linear; */
|
|
position: fixed;
|
|
width: 100%;
|
|
}
|
|
|
|
.xNavbarContent {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.xNavBarLeft {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
}
|
|
|
|
.xNavBarRight {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
}
|
|
|
|
.xNavBarTtitle {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: 100%;
|
|
}
|
|
</style> |