171 lines
3.0 KiB
Plaintext
171 lines
3.0 KiB
Plaintext
|
|
<template>
|
|||
|
|
<view
|
|||
|
|
class="cl-sticky-wrapper"
|
|||
|
|
:style="{
|
|||
|
|
height: rect.height == 0 ? 'auto' : rect.height + 'px',
|
|||
|
|
zIndex
|
|||
|
|
}"
|
|||
|
|
>
|
|||
|
|
<view
|
|||
|
|
class="cl-sticky"
|
|||
|
|
:class="[
|
|||
|
|
{
|
|||
|
|
'is-active': isSticky
|
|||
|
|
}
|
|||
|
|
]"
|
|||
|
|
:style="{
|
|||
|
|
width: isSticky ? rect.width + 'px' : '100%',
|
|||
|
|
left: isSticky ? rect.left + 'px' : 0,
|
|||
|
|
top: stickyTop + 'px'
|
|||
|
|
}"
|
|||
|
|
>
|
|||
|
|
<slot></slot>
|
|||
|
|
<slot name="content" :is-sticky="isSticky"></slot>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script lang="ts" setup>
|
|||
|
|
import { isEmpty, isHarmony, router } from "@/cool";
|
|||
|
|
import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref, watch } from "vue";
|
|||
|
|
import { usePage } from "../../hooks";
|
|||
|
|
|
|||
|
|
defineOptions({
|
|||
|
|
name: "cl-sticky"
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
defineSlots<{
|
|||
|
|
default(): any;
|
|||
|
|
content(props: { isSticky: boolean }): any;
|
|||
|
|
}>();
|
|||
|
|
|
|||
|
|
const props = defineProps({
|
|||
|
|
offsetTop: {
|
|||
|
|
type: Number,
|
|||
|
|
default: 0
|
|||
|
|
},
|
|||
|
|
zIndex: {
|
|||
|
|
type: Number,
|
|||
|
|
default: 100
|
|||
|
|
},
|
|||
|
|
scrollTop: {
|
|||
|
|
type: Number,
|
|||
|
|
default: 0
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const { proxy } = getCurrentInstance()!;
|
|||
|
|
|
|||
|
|
// cl-page 上下文
|
|||
|
|
const { onScroll } = usePage();
|
|||
|
|
|
|||
|
|
// 表示元素的位置信息
|
|||
|
|
type Rect = {
|
|||
|
|
height: number; // 高度
|
|||
|
|
width: number; // 宽度
|
|||
|
|
left: number; // 距离页面左侧的距离
|
|||
|
|
top: number; // 距离页面顶部的距离
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 存储当前sticky元素的位置信息
|
|||
|
|
const rect = reactive<Rect>({
|
|||
|
|
height: 0,
|
|||
|
|
width: 0,
|
|||
|
|
left: 0,
|
|||
|
|
top: 0
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 当前页面滚动的距离
|
|||
|
|
const scrollTop = ref(0);
|
|||
|
|
|
|||
|
|
// 计算属性,判断当前是否处于吸顶状态
|
|||
|
|
const isSticky = computed(() => {
|
|||
|
|
if (rect.height == 0) return false;
|
|||
|
|
|
|||
|
|
return scrollTop.value >= rect.top;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 计算属性,返回sticky元素的top值(吸顶时的偏移量)
|
|||
|
|
const stickyTop = computed(() => {
|
|||
|
|
if (isSticky.value) {
|
|||
|
|
let v = 0;
|
|||
|
|
|
|||
|
|
// #ifdef H5
|
|||
|
|
// H5端默认导航栏高度为44
|
|||
|
|
if (!router.isCustomNavbarPage()) {
|
|||
|
|
v = 44;
|
|||
|
|
}
|
|||
|
|
// #endif
|
|||
|
|
|
|||
|
|
return v + props.offsetTop;
|
|||
|
|
} else {
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 获取sticky元素的位置信息,并更新rect
|
|||
|
|
function getRect() {
|
|||
|
|
nextTick(() => {
|
|||
|
|
setTimeout(
|
|||
|
|
() => {
|
|||
|
|
uni.createSelectorQuery()
|
|||
|
|
.in(proxy)
|
|||
|
|
.select(".cl-sticky")
|
|||
|
|
.boundingClientRect()
|
|||
|
|
.exec((nodes) => {
|
|||
|
|
if (isEmpty(nodes)) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const node = nodes[0] as NodeInfo;
|
|||
|
|
|
|||
|
|
// 赋值时做空值处理,保证类型安全
|
|||
|
|
rect.height = node.height ?? 0;
|
|||
|
|
|
|||
|
|
rect.width = node.width ?? 0;
|
|||
|
|
rect.left = node.left ?? 0;
|
|||
|
|
// top需要减去offsetTop并加上当前滚动距离,保证吸顶准确
|
|||
|
|
rect.top = (node.top ?? 0) - props.offsetTop + scrollTop.value;
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
isHarmony() ? 300 : 0
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
onMounted(() => {
|
|||
|
|
// 获取元素位置信息
|
|||
|
|
getRect();
|
|||
|
|
|
|||
|
|
// 监听页面滚动事件
|
|||
|
|
onScroll((top) => {
|
|||
|
|
scrollTop.value = top;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 监听参数变化
|
|||
|
|
watch(
|
|||
|
|
computed(() => props.scrollTop),
|
|||
|
|
(top: number) => {
|
|||
|
|
scrollTop.value = top;
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
immediate: true
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
defineExpose({
|
|||
|
|
getRect
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.cl-sticky {
|
|||
|
|
@apply relative w-full;
|
|||
|
|
|
|||
|
|
&.is-active {
|
|||
|
|
@apply fixed w-full;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|