实践积累|实践积累 —— 用Vue3简单写一个单行横向滚动组件

目录 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

  • 效果图
  • 需求分析
  • 实现分析
    • 样式展示分析
    • 变量分析
    • 方法分析
  • 实现步骤
      1. 实现模板
      1. 实现css
      1. 首先获取list
      1. 页面挂载后监听groupBoxRef的scroll事件并获取当前的滚动位置
      1. 计算展示的宽度显隐箭头,当卡片宽度大于外层宽度就展示
      1. 控制箭头展示方向
      1. 监听外层宽度改变和窗口大小改变箭头显隐
  • 完整代码
效果图 把之前完成的一个效果图摘出来记录一下,核心代码在这里,如果项目中用到其他的技术,也很容易改。
实践积累|实践积累 —— 用Vue3简单写一个单行横向滚动组件
文章图片

需求分析
  1. 展示数据始终一行,多余的部分可以出滚动条,同时隐藏滚动条样式。
  2. 支持笔记本触控滑动展示
  3. 支持鼠标点击滑动,多余的时候出现箭头按钮,默认滑动3个卡片的位置,顶头就切换方向
  4. 页面出现变动的时候要监听及时显示或隐藏按钮
实现分析 样式展示分析
  • 外层控制总体组件宽度
    • 内层箭头区域展示,内部使用flex布局,绝对定位到右侧
      • 内部箭头svg图标,垂直居中
    • 【实践积累|实践积累 —— 用Vue3简单写一个单行横向滚动组件】内层控制滚动区域宽度,内部使用flex布局,控制一层展示,溢出滚动展示,并隐藏滚动条
      • 内部确定卡片宽高和间距,最后一个右边距为0
    变量分析
  • 卡片 list:Array
  • 控制箭头显隐 arrowShow:Boolean,控制箭头方向 direction:String
  • 获取滚动位置 scrollPosition = {left: 0, top: 0}
  • 计算宽度需要的ref:控制滚动条层 groupBoxRef,卡片 groupCardRef
方法分析
  1. 获取list(可以http,也可以props,根据需求来定)
  2. 页面挂载之后,监听groupBoxRef的scroll事件和窗口变化的resize事件
  3. 箭头的显隐判断方法,改变箭头方向的方法
  4. 鼠标点击箭头的方法
实现步骤 1. 实现模板

2. 实现css
.index-group-box { padding-right: 16px; position: relative; box-sizing: border-box; width: 100%; }.scrollX { width: 16px; position: absolute; top: 0; right: 0; height: 100%; background-color: #512D6D; display: flex; justify-content: center; align-items: center }.scrollX:hover { cursor: pointer; background-color: #65447d; }.index-group-boxIn { display: flex; scroll-behavior: smooth; white-space: nowrap; overflow-x: auto; flex: none; scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* IE 10+ */ }.index-group-boxIn::-webkit-scrollbar { display: none; /* Chrome Safari */ }.group-card { padding: 8px 16px; box-sizing:border-box; width: 200px; height: 100px; border-radius: 4px; margin-right: 16px; flex: none; background: #71EFA3; color: #54436B; }.group-card span{ color: #54436B; }.group-card:hover{ background: #ACFFAD; }.group-card:nth-last-of-type(1){ margin-right: 0px; }

3. 首先获取list

4. 页面挂载后监听groupBoxRef的scroll事件并获取当前的滚动位置
// 添加reactive和onMounted import { defineComponent, ref, reactive, onMounted } ... const groupBoxRef = ref(null); // 获取外层卡?ref const groupCardRef = ref(null); // 获取卡?ref const scrollPosition = reactive({ left: 0, top: 0 }); // 滚动位置 ... // 获取scroll函数的位置 const handleScroll = e => { scrollPosition.left = e.target.scrollLeft; scrollPosition.top = e.target.scrollTop; }getMyGroup(); onMounted(() => { // 监听scroll事件 groupBoxRef.value.addEventListener('scroll', handleScroll, true); })return { // data groupInfo, // ref groupBoxRef, groupCardRef, };

5. 计算展示的宽度显隐箭头,当卡片宽度大于外层宽度就展示
  • 卡片宽度:groupCardRef.value.offsetWidth
  • 外层宽度:groupBoxRef.value.offsetWidth
  • 滚动区域宽度:卡片数量 * (卡片宽度 + 右边距)- 最后一个右边距
实践积累|实践积累 —— 用Vue3简单写一个单行横向滚动组件

... const arrowShow = ref(false); // 滚动箭头是否显示// 获取卡?宽度,第?个参数是卡?个数,默认是整个数组,第?个参数是剩余的margin const getWidth = (num = groupInfo.value.length, restMargin = 16) => { // 如果没有内容就返回0 if(!groupCardRef.value) return 0; return num * (groupCardRef.value.offsetWidth + 16) - restMargin; }// 判断arrow是否展示 const checkArrowShow = () => { arrowShow.value = https://www.it610.com/article/getWidth()> groupBoxRef.value?.offsetWidth ? true : false; } ... onMounted(() => { // 监听scroll事件 groupBoxRef.value.addEventListener('scroll', handleScroll, true); // 首次检查箭头展示 checkArrowShow(); })

6. 控制箭头展示方向
  • 初始朝右,横向滚动区域为0就朝右,剩余宽度比外层宽度小就朝左
  • 剩余宽度:滚动区域宽度 - 滚动距离
实践积累|实践积累 —— 用Vue3简单写一个单行横向滚动组件 实践积累|实践积累 —— 用Vue3简单写一个单行横向滚动组件

... const direction = ref('right'); // 默认项?组箭头向右 ... // 改变滚动?向 const changeArrow = (scrollLeft) => { // 默认获取scoll部分整个宽度 const getScrollWidth = getWidth(); // 计算得出剩余宽度 const restWidth = getScrollWidth - scrollLeft if (restWidth <= groupBoxRef.value.offsetWidth) { direction.value = 'https://www.it610.com/article/left' } else if ( scrollLeft === 0 ) { direction.value = 'https://www.it610.com/article/right' } }// ?标点击滚动 const groupScroll = async () => { // 计算移动宽度,现在是移动3个卡片的数量 const getMoveWidth = getWidth(3, 0); // 如果方向是右边就+,左边就- if (direction.value =https://www.it610.com/article/=='right') { groupBoxRef.value.scrollLeft += getMoveWidth; } else { groupBoxRef.value.scrollLeft -= getMoveWidth; } // 滚动需要时间才能获取最新的距离,根据新的距离看箭头的方向 setTimeout(() => { changeArrow(groupBoxRef.value.scrollLeft); }, 500) }// 触摸板滑动的时候位置实时改变箭头方向 const handleScroll = e => { ... changeArrow(scrollPosition.left); }return {// 新加的data ... direction, // ref ... // 新加的methods groupScroll };

7. 监听外层宽度改变和窗口大小改变箭头显隐
import { defineComponent, ref, reactive, onMounted, watchEffect } from 'vue'; ... watchEffect(() => { checkArrowShow(); })onMounted(() => { ... // 监听窗?变化事件,判断arrow的展示 window.addEventListener('resize', checkArrowShow, true); })

完整代码
.index-group-box { padding-right: 16px; position: relative; box-sizing: border-box; width: 100%; }.scrollX { width: 16px; position: absolute; top: 0; right: 0; height: 100%; background-color: #512D6D; display: flex; justify-content: center; align-items: center }.scrollX:hover { cursor: pointer; background-color: #65447d; }.index-group-boxIn { display: flex; scroll-behavior: smooth; white-space: nowrap; overflow-x: auto; flex: none; scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* IE 10+ */ }.index-group-boxIn::-webkit-scrollbar { display: none; /* Chrome Safari */ }.group-card { padding: 8px 16px; box-sizing:border-box; width: 200px; height: 100px; border-radius: 4px; margin-right: 16px; flex: none; background: #71EFA3; color: #54436B; }.group-card span{ color: #54436B; }.group-card:hover{ background: #ACFFAD; }.group-card:nth-last-of-type(1){ margin-right: 0px; }

    推荐阅读