layui/.svn/pristine/4f/4fc472c22addcb9fad7d0a94abc3adaa95c02bdd.svn-base
2022-12-09 16:41:41 +08:00

214 lines
5.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
class="layui-scroll"
:class="{ hide: data.winWidth < 500 }"
:style="{ height: height }"
>
<div class="layui-scroll-y">
<div ref="scrollRef" class="layui-scroll-wrap" @scroll="onMosewheel">
<slot></slot>
</div>
<div
ref="barRef"
class="layui-scroll-track"
:style="{
backgroundColor: data.heightPre == 1 ? 'transparent' : trackColor,
}"
>
<div
:style="{
height: data.barHeight + 'px',
width: thumbWidth + 'px',
transform: 'translateY(' + data.translateY + 'px)',
backgroundColor: data.heightPre == 1 ? 'transparent' : thumbColor,
}"
class="layui-scroll-thumb"
@mousedown.stop.prevent="moveStart"
></div>
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
name: "LayScroll",
};
</script>
<script lang="ts" setup>
import "./index.less";
import { onMounted, nextTick, reactive, onUnmounted, ref } from "vue";
export interface ScrollProps {
height?: string;
trackColor?: string;
thumbColor?: string;
thumbWidth?: number;
}
const props = withDefaults(defineProps<ScrollProps>(), {
height: "100%",
trackColor: "rgba(0,0,0,0)",
thumbColor: "#eeeeee",
thumbWidth: 6,
});
const emit = defineEmits(["arrive"]);
const scrollRef = ref<HTMLElement | null | undefined>();
const barRef = ref<HTMLElement | null | undefined>();
const data = reactive({
translateY: 0, // 滚动块平移的距离
heightPre: 0, // 可视高度和内容高度比
barHeight: 0, // 滑块高度
winWidth: document.body.clientWidth, //初始化浏览器页面宽度
});
let time: NodeJS.Timeout; // 定时器
let isMove = false; // 判断鼠标是否点击滑块(为松开)
let moveClientY = 0; // 鼠标点击滑块时,相对滑块的位置
let trackHeight = 0; // 滚动条轨道高度
let wrapHeight = 0; // 容器高度(可视高度)
let wrapContentHeight = 0; // 内容高度(可滚动内容的高度)
onMounted(() => {
monitorWindow();
monitorScrollBar();
nextTick(() => {
calculationLength();
});
});
// 页面卸载清除定时器
onUnmounted(() => {
window.clearInterval(time);
});
// 监听页面尺寸改变计算滚动条
const monitorWindow = function () {
let time: NodeJS.Timeout;
window.addEventListener("resize", () => {
data.winWidth = document.body.clientWidth;
clearTimeout(time);
time = setTimeout(() => {
initScrollListner();
}, 500);
});
};
//监听内容元素尺寸变化
const monitorScrollBar = function () {
// @ts-ignore
let MutationObserver =
window.MutationObserver ||
// @ts-ignore
window.WebKitMutationObserver ||
// @ts-ignore
window.MozMutationObserver;
const observer: any = new MutationObserver((mutations) => {
initScrollListner();
});
observer.observe(scrollRef.value, {
attributes: true,
childList: true,
subtree: true,
});
};
// 初始化延迟监听滚动条
const calculationLength = function () {
// 直接执行initScrollListner函数获取滚动条长度部准确
// 因为页面渲染有延迟获取dom元素需要延迟
// 每间隔10毫秒更新滑块长度
time = setInterval(() => {
// 计算滚动条长度
initScrollListner();
}, 50);
// 间隔500毫秒清除定时器滑块缩短会有动画效果时间可延长没有影响
setTimeout(() => {
window.clearInterval(time);
}, 2000);
};
// 计算滚动条高度
const initScrollListner = function () {
let scroll = scrollRef.value;
let bar = barRef.value;
// scroll有时候拿不到元素要判断一下
if (scroll && bar) {
wrapContentHeight = scroll.scrollHeight;
wrapHeight = scroll.clientHeight;
trackHeight = bar.clientHeight;
data.heightPre = wrapHeight / wrapContentHeight;
data.barHeight = data.heightPre * trackHeight;
}
};
// 内容滚动时,计算滑块移动的距离
const onMosewheel = (e: any) => {
data.translateY = e.target.scrollTop * data.heightPre;
if (data.translateY == 0) {
// 到达顶部
arrive("top");
} else if (
e.target.scrollTop + e.target.offsetHeight ==
e.target.scrollHeight
) {
// 滚出高度 + 可视区域高度 == 内容高度
arrive("bottom");
}
};
// 到达顶部或者底部通知父级元素
const arrive = (tb: string) => {
emit("arrive", tb);
};
// 鼠标点击滑块时
const moveStart = (e: any) => {
isMove = true;
// clientY当鼠标事件发生时鼠标相对于浏览器这里说的是浏览器的有效区域y轴的位置
// data.translateY 滚动块平移的距离
// moveClientY 鼠标点击滑块时,相对滑块的位置
moveClientY = e.clientY - data.translateY;
moveTo(); //移动时
moveEnd(); //鼠标松开时
};
// 鼠标移动改变thumb的位置以及容器scrollTop的位置
const moveTo = () => {
document.onmousemove = (e) => {
// 移动时候判断是不是松开,松开就不在执行滑块移动操作
if (isMove) {
// 移动滑块时,判断时候到达顶部或者底部
if (e.clientY - moveClientY > trackHeight - data.barHeight) {
// 滑块到达 底部 就不在改变滑块translateY值
data.translateY = trackHeight - data.barHeight;
} else if (e.clientY - moveClientY < 0) {
// 滑块到达 顶部 就不在改变滑块 translateY 值
data.translateY = 0;
} else {
//改变滑块位置
data.translateY = e.clientY - moveClientY;
}
// 计算出内容盒子滚出顶部的距离
if (scrollRef.value) {
scrollRef.value.scrollTop = data.translateY / data.heightPre;
}
}
};
};
// 鼠标从滑块松开时,不在监听滑块移动操作
const moveEnd = function () {
document.onmouseup = (e) => {
if (isMove) {
isMove = false;
}
};
};
</script>