<template>
<div class="scrollable">
<div class="content" ref="content">
<slot></slot>
</div>
<div ref="scrollBar" class="scrollBar" :style="`right:${props.right}px;background:${props.scrollColor}`">
<div :style="`position: relative;height: 100%;`">
<div
class="slider"
@mousedown="startDrag"
@touchstart="startDrags"
:style="`width:200% ;height:${sliderHeight}%;margin-top:${position}px;background:${props.sliderColor};border-radius: 3px;`"
></div>
</div>
</div>
<!-- <div class="scrollBar" ref="scrollBar" :style="`right:${props.right}px;background:${props.scrollColor};`">
<div class="slider" :style="`height:${sliderHeight}%;margin-top:${position}px;background:${props.sliderColor};`"></div>
</div> -->
</div>
</template>
<script setup>
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue';
const props = defineProps({
scrollColor: {
type: String,
default: '',
},
sliderColor: {
type: String,
default: 'linear-gradient(#697083, #f1f3f4)',
},
data: {
type: Array,
default: [],
},
right: {
type: String,
default: '0',
},
});
const content = ref(null); // ref内容
const scrollBar = ref(null); // ref滚动条
const contentCH = ref(0); // content盒子高度
const contentSH = ref(0); // content内容高度
const scrollBarCH = ref(0); // 滚动条高度
const activeScrollDistance = ref(0); // 内容滚动的距离
const contentScrollDistance = ref(0); // 内容滚动的距离
const sliderHeight = ref(0); // 滑块高度
const position = ref(0); // 滚动条滑动距离
const isDragging = ref(false); //是否正在拖动滑块
const dragStartY = ref(0); //拖动起始位置的垂直坐标
const dragStartPos = ref(0); //拖动起始时滑块的位置
const isTouching = ref(false); //是否正在触摸滑块
// const touchStartY = ref(0); //触摸起始位置的垂直坐标
// const touchStartPos = ref(0); //触摸起始时滑块的位置
// const contentScrollTop = ref(0); // 内容区域的滚动位置
// 内容滚动时的操作
const handleScroll = () => {
if (isDragging.value || isTouching.value) return; // 正在拖动滑块时不执行滚动操作
const { scrollTop } = content.value;
position.value = (scrollTop * activeScrollDistance.value) / contentScrollDistance.value; // 滑块需要滑动的距离
content.value.scrollTop = (position.value * contentScrollDistance.value) / activeScrollDistance.value; // 更新内容区域的滚动位置
console.log(content.value.scrollTop, '11');
};
const handle = () => {
console.log(123);
};
// 开始拖动滑块
const startDrag = (event) => {
isDragging.value = true;
dragStartY.value = event.clientY;
dragStartPos.value = position.value;
document.addEventListener('mousemove', handleDrag);
document.addEventListener('mouseup', endDrag);
};
// 处理滑块拖动过程
const handleDrag = (event) => {
// console.log(123123 , event);
if (!isDragging.value) return;
const deltaY = event.clientY - dragStartY.value;
const newPos = dragStartPos.value + deltaY;
position.value = Math.max(0, Math.min(newPos, activeScrollDistance.value));
content.value.scrollTop = (position.value * contentScrollDistance.value) / activeScrollDistance.value;
};
//结束滑块拖动
const endDrag = () => {
isDragging.value = false;
document.removeEventListener('mousemove', handleDrag);
document.removeEventListener('mouseup', endDrag);
};
// 触屏开始拖动滑块
const startDrags = (event) => {
isDragging.value = true;
dragStartY.value = event.touches[0].clientY;
dragStartPos.value = position.value;
document.addEventListener('touchmove', handleTouchMove);
document.addEventListener('touchend', handleTouchEnd);
};
// 触屏处理滑块拖动过程
const handleTouchMove = (event) => {
if (!isDragging.value) return;
const deltaY = event.touches[0].clientY - dragStartY.value;
const newPos = dragStartPos.value + deltaY;
position.value = Math.max(0, Math.min(newPos, activeScrollDistance.value));
content.value.scrollTop = (position.value * contentScrollDistance.value) / activeScrollDistance.value;
};
// 触屏结束滑块拖动
const handleTouchEnd = () => {
isDragging.value = false;
document.removeEventListener('touchmove', handleTouchMove);
document.removeEventListener('touchend', handleTouchEnd);
};
onMounted(() => {
watch(
() => props.data,
() => {
// console.log('data:123', props.data);
nextTick(() => {
const { clientHeight, scrollHeight } = content.value;
console.log('容器的高度:', clientHeight, '内容高度:', scrollHeight);
contentCH.value = clientHeight;
contentSH.value = scrollHeight;
scrollBarCH.value = scrollBar.value.clientHeight;
sliderHeight.value = (clientHeight / scrollHeight) * 100;
activeScrollDistance.value = scrollBarCH.value - scrollBarCH.value * (sliderHeight.value / 100);
contentScrollDistance.value = contentSH.value - contentCH.value;
content.value.addEventListener('scroll', handleScroll);
});
},
{ immediate: true, deep: true }
);
});
onBeforeUnmount(() => {
// 移除监听
content.value.removeEventListener('scroll', handleScroll);
});
</script>
<style scoped>
.scrollable {
position: relative;
display: flex;
height: 100%;
width: 100%;
}
.content {
width: 100%;
overflow: auto;
}
.content::-webkit-scrollbar {
display: none;
width: 0;
}
.scrollBar {
position: absolute;
top: 0;
right: 10px;
width: 5px;
height: 100%;
border-radius: 3px;
}
.slider {
width: 100%;
border-radius: 3px;
position: absolute;
left: -2px;
}
</style>
页面中(用时修改一下)
<div class="content" ref="content">
<scroll :data="homepagelist" right="" scrollColor="#3a405bFF">
<div style="display: flex;flex-wrap: wrap">
<div class="homePage-item" @click="handleBasic(index)" v-for="(item, index) in homepagelist"
:key="index">
<div class="price"></div>
<div class="basicS">{{ item.name }}</div>
<div class="InDetail">{{ item.titlehead }}</div>
</div>
</div>
</scroll>
</div>
<!-- scoll滚动条 -->
<template>
<div class="scrollable">
<div class="content" ref="content">
<slot></slot>
</div>
<div ref="scrollBar" class="scrollBar" :style="`right:${props.right}px;background:${props.scrollColor};${isShowScrollBar()}`">
<div :style="`position: relative;height: 100%;`">
<div class="slider" @mousedown="startDrag" @touchstart="startDrags" :style="`width:200% ;height:${sliderHeight}%;margin-top:${position}px;background:${props.sliderColor};border-radius: 3px;`"></div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue';
const props = defineProps({
scrollColor: {
type: String,
default: '',
},
sliderColor: {
type: String,
default: 'linear-gradient(#697083, #f1f3f4)',
},
data: {
type: Array,
default: [],
},
right: {
type: String,
default: '0',
},
});
const content = ref(null); // ref内容
const scrollBar = ref(null); // ref滚动条
const contentCH = ref(0); // content盒子高度
const contentSH = ref(0); // content内容高度
const scrollBarCH = ref(0); // 滚动条高度
const activeScrollDistance = ref(0); // 内容滚动的距离
const contentScrollDistance = ref(0); // 内容滚动的距离
const sliderHeight = ref(0); // 滑块高度
const position = ref(0); // 滚动条滑动距离
const isDragging = ref(false); //是否正在拖动滑块
const dragStartY = ref(0); //拖动起始位置的垂直坐标
const dragStartPos = ref(0); //拖动起始时滑块的位置
const isTouching = ref(false); //是否正在触摸滑块
const isShowScrollBar = () => `${contentCH.value == contentSH.value ? 'opacity:0;' : ''}`; // 是否显示滚动条
// 内容滚动时的操作
const handleScroll = () => {
if (isDragging.value || isTouching.value) return; // 正在拖动滑块时不执行滚动操作
const { scrollTop } = content.value;
position.value = (scrollTop * activeScrollDistance.value) / contentScrollDistance.value; // 滑块需要滑动的距离
content.value.scrollTop = (position.value * contentScrollDistance.value) / activeScrollDistance.value; // 更新内容区域的滚动位置
};
// 开始拖动滑块
const startDrag = (event) => {
event.preventDefault(); // 阻止默认事件
isDragging.value = true;
dragStartY.value = event.clientY;
dragStartPos.value = position.value;
document.addEventListener('mousemove', handleDrag);
document.addEventListener('mouseup', endDrag);
};
// 处理滑块拖动过程
const handleDrag = (event) => {
if (!isDragging.value) return;
const deltaY = event.clientY - dragStartY.value;
const newPos = dragStartPos.value + deltaY;
position.value = Math.max(0, Math.min(newPos, activeScrollDistance.value));
content.value.scrollTop = (position.value * contentScrollDistance.value) / activeScrollDistance.value;
};
//结束滑块拖动
const endDrag = () => {
if (!isDragging.value) return;
isDragging.value = false;
document.removeEventListener('mousemove', handleDrag);
window.removeEventListener('mouseup', endDrag);
};
// 触屏开始拖动滑块
const startDrags = (event) => {
isDragging.value = true;
dragStartY.value = event.touches[0].clientY;
dragStartPos.value = position.value;
document.addEventListener('touchmove', handleTouchMove);
document.addEventListener('touchend', handleTouchEnd);
};
// 触屏处理滑块拖动过程
const handleTouchMove = (event) => {
if (!isDragging.value) return;
console.log('start2');
const deltaY = event.touches[0].clientY - dragStartY.value;
const newPos = dragStartPos.value + deltaY;
position.value = Math.max(0, Math.min(newPos, activeScrollDistance.value));
content.value.scrollTop = (position.value * contentScrollDistance.value) / activeScrollDistance.value;
};
// 触屏结束滑块拖动
const handleTouchEnd = () => {
console.log('end2');
isDragging.value = false;
document.removeEventListener('touchmove', handleTouchMove);
document.removeEventListener('touchend', handleTouchEnd);
};
onMounted(() => {
watch(
() => props.data,
() => {
// console.log('scoll监听数组变化', props.data);
nextTick(() => {
const { clientHeight, scrollHeight } = content.value;
// console.log('容器的高度:', clientHeight, '内容高度:', scrollHeight);
contentCH.value = clientHeight;
contentSH.value = scrollHeight;
scrollBarCH.value = scrollBar.value.clientHeight;
sliderHeight.value = (clientHeight / scrollHeight) * 100;
activeScrollDistance.value = scrollBarCH.value - scrollBarCH.value * (sliderHeight.value / 100);
contentScrollDistance.value = contentSH.value - contentCH.value;
content.value.addEventListener('scroll', handleScroll);
});
},
{ immediate: true, deep: true }
);
});
onBeforeUnmount(() => {
// 移除监听
content.value.removeEventListener('scroll', handleScroll);
});
</script>
<style scoped>
.scrollable {
position: relative;
display: flex;
height: 100%;
width: 100%;
}
.content {
width: 100%;
overflow: auto;
}
.content::-webkit-scrollbar {
display: none;
width: 0;
}
.scrollBar {
position: absolute;
top: 0;
right: 10px;
width: 5px;
height: 100%;
border-radius: 3px;
}
.slider {
width: 100%;
border-radius: 3px;
position: absolute;
left: -2px;
}
</style>
css 多行省略号
height: 140px;
word-break: break-all;/* 兼容 数字、英文 不换行 */
line-break: anywhere;/* 中文字符隔空换行 */
display: -webkit-box;/* 特别显示模式,将对象作为弹性伸缩盒子模型显示 */
-webkit-box-orient: vertical;/* 盒子中内容竖直排列 */
overflow: hidden;
text-overflow: ellipsis;/* 文本溢出部分用省略号表示 */
-webkit-line-clamp: 4; /* 行数 */