178 lines
4.9 KiB
Vue
178 lines
4.9 KiB
Vue
<template>
|
||
<view>
|
||
<view
|
||
class="u-sticky-wrap"
|
||
:class="[elClass, customClass]"
|
||
:style="
|
||
$u.toStyle(
|
||
{
|
||
height: fixed ? height + 'px' : 'auto',
|
||
backgroundColor: bgColor
|
||
},
|
||
customStyle
|
||
)
|
||
"
|
||
>
|
||
<view
|
||
class="u-sticky"
|
||
:style="{
|
||
position: fixed ? 'fixed' : 'static',
|
||
top: stickyTop + 'px',
|
||
left: left + 'px',
|
||
width: width == 'auto' ? 'auto' : width + 'px',
|
||
zIndex: uZIndex
|
||
}"
|
||
>
|
||
<slot></slot>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script lang="ts">
|
||
export default {
|
||
name: 'u-sticky',
|
||
options: {
|
||
addGlobalClass: true,
|
||
// #ifndef MP-TOUTIAO
|
||
virtualHost: true,
|
||
// #endif
|
||
styleIsolation: 'shared'
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, watch, nextTick, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue';
|
||
import { $u } from '../..';
|
||
import { StickyProps } from './types';
|
||
|
||
/**
|
||
* sticky 吸顶
|
||
* @description 该组件与CSS中position: sticky属性实现的效果一致,当组件达到预设的到顶部距离时, 就会固定在指定位置,组件位置大于预设的顶部距离时,会重新按照正常的布局排列。
|
||
* @tutorial https://uviewpro.cn/zh/components/sticky.html
|
||
* @property {String|Number} offsetTop 吸顶时与顶部的距离,单位rpx(默认0)
|
||
* @property {String|Number} index 自定义标识,用于区分是哪一个组件
|
||
* @property {Boolean} enable 是否开启吸顶功能(默认true)
|
||
* @property {String} bgColor 组件背景颜色(默认var(--u-bg-white))
|
||
* @property {String|Number} zIndex 吸顶时的z-index值(默认970)
|
||
* @property {String|Number} h5NavHeight 导航栏高度,自定义导航栏时(无导航栏时需设置为0),需要传入此值,单位px(默认44)
|
||
* @event fixed 组件吸顶时触发
|
||
* @event unfixed 组件取消吸顶时触发
|
||
* @example <u-sticky offset-top="200"><view>塞下秋来风景异,衡阳雁去无留意</view></u-sticky>
|
||
*/
|
||
const props = defineProps(StickyProps);
|
||
|
||
const emit = defineEmits(['fixed', 'unfixed']);
|
||
|
||
const instance = getCurrentInstance();
|
||
const fixed = ref(false);
|
||
const height = ref<'auto' | number>('auto');
|
||
const stickyTop = ref(0);
|
||
const elClass = ref('');
|
||
const left = ref(0);
|
||
const width = ref<'auto' | number>('auto');
|
||
let contentObserver: any = null;
|
||
|
||
elClass.value = $u.guid();
|
||
|
||
const uZIndex = computed(() => (props.zIndex ? props.zIndex : ($u?.zIndex?.sticky ?? 970)));
|
||
|
||
watch(
|
||
() => props.offsetTop,
|
||
() => {
|
||
initObserver();
|
||
}
|
||
);
|
||
|
||
watch(
|
||
() => props.enable,
|
||
val => {
|
||
if (val == false) {
|
||
fixed.value = false;
|
||
disconnectObserver('contentObserver');
|
||
} else {
|
||
initObserver();
|
||
}
|
||
}
|
||
);
|
||
|
||
onMounted(() => {
|
||
initObserver();
|
||
});
|
||
|
||
onBeforeUnmount(() => {
|
||
disconnectObserver('contentObserver');
|
||
});
|
||
|
||
/**
|
||
* 初始化 IntersectionObserver 监听
|
||
*/
|
||
function initObserver() {
|
||
if (!props.enable) return;
|
||
// #ifdef H5
|
||
stickyTop.value =
|
||
Number(props.offsetTop) !== 0
|
||
? uni.upx2px(Number(props.offsetTop)) + Number(props.h5NavHeight)
|
||
: Number(props.h5NavHeight);
|
||
// #endif
|
||
// #ifndef H5
|
||
stickyTop.value = Number(props.offsetTop) !== 0 ? uni.upx2px(Number(props.offsetTop)) : 0;
|
||
// #endif
|
||
disconnectObserver('contentObserver');
|
||
$u.getRect('.' + elClass.value, instance).then((res: any) => {
|
||
height.value = res.height;
|
||
left.value = res.left;
|
||
width.value = res.width;
|
||
nextTick(() => {
|
||
observeContent();
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 创建 IntersectionObserver 监听内容区域
|
||
*/
|
||
function observeContent() {
|
||
disconnectObserver('contentObserver');
|
||
|
||
contentObserver = uni.createIntersectionObserver(instance?.proxy, {
|
||
thresholds: [0.95, 0.98, 1]
|
||
});
|
||
contentObserver.relativeToViewport({
|
||
top: -stickyTop.value
|
||
});
|
||
contentObserver.observe('.' + elClass.value, (res: any) => {
|
||
if (!props.enable) return;
|
||
setFixed(res.boundingClientRect.top);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 设置 fixed 状态
|
||
*/
|
||
function setFixed(top: number) {
|
||
const isFixed = top < stickyTop.value;
|
||
if (isFixed) emit('fixed', props.index);
|
||
else if (fixed.value) emit('unfixed', props.index);
|
||
fixed.value = isFixed;
|
||
}
|
||
|
||
/**
|
||
* 断开 observer
|
||
*/
|
||
function disconnectObserver(observerName: string) {
|
||
if (observerName === 'contentObserver' && contentObserver) {
|
||
contentObserver.disconnect();
|
||
contentObserver = null;
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
@import '../../libs/css/style.components.scss';
|
||
.u-sticky {
|
||
z-index: 9999999999;
|
||
}
|
||
</style>
|