小程序初始提交
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user