layui/.svn/pristine/7e/7ecbd73e2e9a5f0d0b553b51b8cf2ebd49e6d218.svn-base
2022-12-09 16:41:41 +08:00

138 lines
3.5 KiB
Plaintext

<template>
<div class="layui-affix" :style="getStyle" ref="dom">
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "LayAffix",
};
</script>
<script setup lang="ts">
import "./index.less";
import {
ref,
onMounted,
onUnmounted,
nextTick,
computed,
StyleValue,
} from "vue";
export interface AiffxProps {
offset?: number;
target?: HTMLElement;
position?: string;
}
const props = withDefaults(defineProps<AiffxProps>(), {
offset: 0,
position: "top",
target: () => {
return document.body;
},
});
const emit = defineEmits(["scroll"]);
const outWindow = ref(false);
const dom = ref();
let changeScrollTop = 0;
let orginOffsetLeft = 0;
let orginOffsetTop = 0;
let marginLeft = 0;
let marginTop = 0;
let marginBottom = 0;
let fixedOffset = 0;
const getStyle = computed(() => {
if (outWindow.value && dom.value) {
let style = {
position: "fixed !important",
top: "unset",
bottom: "unset",
left: orginOffsetLeft - marginLeft + "px",
};
if (props.position === "top") {
style.top = fixedOffset - marginTop + "px";
} else {
style.bottom = fixedOffset - marginBottom + "px";
}
return style as StyleValue;
}
});
//检查是否在窗口内
const checkInWindow = () => {
if (dom.value) {
let offsetTop = dom.value.offsetTop;
let scrollTop = props.target?.scrollTop;
if (props.position === "top") {
//top检查 当前元素与容器顶部距离-减去滚动条的高度+容器offsetTop
let result = offsetTop - scrollTop + props.target.offsetTop;
if (result < fixedOffset) {
if (outWindow.value) {
if (scrollTop <= changeScrollTop) {
outWindow.value = false;
}
} else {
changeScrollTop = scrollTop;
outWindow.value = true;
}
}
} else {
//bottom检查 可视区窗口高度 + 文档滚动高度 - 当前元素与容器顶部距离 - 当前元素高度
let viewHeight =
props.target.offsetHeight > window.innerHeight
? window.innerHeight
: props.target.offsetHeight;
let result = viewHeight + scrollTop - offsetTop - dom.value.offsetHeight;
if (outWindow.value) {
if (scrollTop >= changeScrollTop) {
outWindow.value = false;
}
} else {
if (result < fixedOffset) {
changeScrollTop = scrollTop - result + props.offset;
outWindow.value = true;
}
}
}
emit("scroll", {
targetScroll: scrollTop,
affixed: outWindow.value,
offset: !outWindow.value ? 0 : Math.abs(scrollTop - changeScrollTop),
});
}
};
const getDomStyle = (dom: any, attr: string) => {
if (dom.currentStyle) {
return dom.currentStyle[attr];
} else {
// @ts-ignore
return document.defaultView.getComputedStyle(dom, null)[attr];
}
};
onMounted(() => {
nextTick(() => {
orginOffsetTop = dom.value.offsetTop - props.target.offsetTop;
orginOffsetLeft = dom.value.getBoundingClientRect().left;
marginLeft = parseFloat(getDomStyle(dom.value, "marginLeft"));
marginTop = parseFloat(getDomStyle(dom.value, "marginTop"));
marginBottom = parseFloat(getDomStyle(dom.value, "marginBottom"));
fixedOffset = props.offset + props.target.offsetTop;
if (props.position === "bottom") {
fixedOffset = props.offset;
}
props.target.addEventListener("scroll", checkInWindow, true);
checkInWindow();
});
});
onUnmounted(() => {
props.target.removeEventListener("scroll", checkInWindow);
});
</script>