vue3自定义封装滚动条组件,支持滚轮滑动,点击拖动,触屏拖动

<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; /* 行数 */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值