目录

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

  • 效果图
  • 需要剖析
  • 实现剖析

    • 款式展现剖析
    • 变量剖析
    • 办法剖析
  • 实现步骤

      1. 实现模板
      1. 实现css
      1. 首先获取list
      1. 页面挂载后监听groupBoxRef的scroll事件并获取以后的滚动地位
      1. 计算展现的宽度显隐箭头,当卡片宽度大于外层宽度就展现
      1. 管制箭头展现方向
      1. 监听外层宽度扭转和窗口大小扭转箭头显隐
  • 残缺代码

效果图

把之前实现的一个效果图摘出来记录一下,外围代码在这里,如果我的项目中用到其余的技术,也很容易改。

需要剖析

  1. 展现数据始终一行,多余的局部能够出滚动条,同时暗藏滚动条款式。
  2. 反对笔记本触控滑动展现
  3. 反对鼠标点击滑动,多余的时候呈现箭头按钮,默认滑动3个卡片的地位,顶头就切换方向
  4. 页面呈现变动的时候要监听及时显示或暗藏按钮

实现剖析

款式展现剖析

  • 外层管制总体组件宽度

    • 内层箭头区域展现,外部应用flex布局,相对定位到右侧

      • 外部箭头svg图标,垂直居中
    • 内层管制滚动区域宽度,外部应用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. 实现模板

<template>  <div class="index-group-box">    <!-- 左边滑动箭头 -->    <div class="scrollX">      <img src='../assets/arrow-left-bold.svg'/>    </div>    <!-- 卡⽚ -->    <div class="index-group-boxIn" ref="groupBoxRef">      <div        v-for="item in groupInfo"        :key="item.id"        ref="groupCardRef"        class="group-card"      >        <div class="card-name">          名称          <span>{{ item.name }}</span>        </div>      </div>    </div>  </div></template>

2. 实现css

<style scoped>  .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;  }</style>

3. 首先获取list

<script>import { defineComponent, ref } from 'vue';export default defineComponent({    name: 'scroll',    setup() {        const groupInfo = ref([]);                // 获取卡片列表        const getMyGroup = async () => {            const data = [{                id: 1,                name:'卡片1'            },{                id: 2,                name:'卡片2'            },{                id: 3,                name:'卡片3'            },{                id: 4,                name:'卡片4'            },{                id: 5,                name:'卡片5'            }]            groupInfo.value = data;        }        getMyGroup();        return {            // data            groupInfo,        };    },});</script>

4. 页面挂载后监听groupBoxRef的scroll事件并获取以后的滚动地位

// 增加reactive和onMountedimport { defineComponent, ref, reactive, onMounted } ...const groupBoxRef = ref(null); // 获取外层卡⽚refconst groupCardRef = ref(null); // 获取卡⽚refconst 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
  • 滚动区域宽度:卡片数量 * (卡片宽度 + 左边距)- 最初一个左边距
<div class="scrollX" v-if="arrowShow">    <img src='../assets/arrow-left-bold.svg'/></div>
...const arrowShow = ref(false); // 滚动箭头是否显示// 获取卡⽚宽度,第⼀个参数是卡⽚个数,默认是整个数组,第⼆个参数是残余的marginconst 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 = getWidth() > groupBoxRef.value?.offsetWidth ? true : false;}...onMounted(() => {    // 监听scroll事件    groupBoxRef.value.addEventListener('scroll', handleScroll, true);    // 首次查看箭头展现    checkArrowShow();})

6. 管制箭头展现方向

  • 初始朝右,横向滚动区域为0就朝右,残余宽度比外层宽度小就朝左
  • 残余宽度:滚动区域宽度 - 滚动间隔
<!-- 增加点击箭头事件和箭头方向svg --><div class="scrollX" @click="groupScroll" v-if="arrowShow">    <img v-if="direction === 'left'" src='../assets/arrow-left-bold.svg'/>    <img v-else src='../assets/arrow-right-bold.svg'/></div>
...const direction = ref('right'); // 默认项⽬组箭头向右...// 扭转滚动⽅向const changeArrow = (scrollLeft) => {    // 默认获取scoll局部整个宽度    const getScrollWidth = getWidth();    // 计算得出残余宽度    const restWidth = getScrollWidth - scrollLeft    if (restWidth <= groupBoxRef.value.offsetWidth) {        direction.value = 'left'    } else if ( scrollLeft === 0 ) {        direction.value = 'right'    }}// ⿏标点击滚动const groupScroll = async () => {    // 计算挪动宽度,当初是挪动3个卡片的数量    const getMoveWidth = getWidth(3, 0);    // 如果方向是左边就+,右边就-    if (direction.value === '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);})

残缺代码

<template>    <div class="index-group-box">        <!-- 左边滑动箭头 -->        <div class="scrollX" @click="groupScroll" v-if="arrowShow">            <img v-if="direction === 'left'" src='../assets/arrow-left-bold.svg'/>            <img v-else src='../assets/arrow-right-bold.svg'/>        </div>        <!-- 卡⽚ -->        <div class="index-group-boxIn" ref="groupBoxRef">            <div                v-for="item in groupInfo"                :key="item.id"                ref="groupCardRef"                class="group-card"            >                <div class="card-name">                    名称                    <span>{{ item.name }}</span>                </div>            </div>        </div>    </div></template><script>import { defineComponent, ref, reactive, onMounted, watchEffect } from 'vue';export default defineComponent({    name: 'scroll',    setup() {        const groupInfo = ref([]); // 卡片list        const direction = ref('right'); // 默认箭头向右        const arrowShow = ref(false); // 滚动箭头是否显示        const groupBoxRef = ref(null); // 获取外层卡⽚ref        const groupCardRef = ref(null); // 获取卡⽚ref        const scrollPosition = reactive({            left: 0,            top: 0        }); // 滚动地位          // 获取卡片列表        const getMyGroup = async () => {            const data = [{                id: 1,                name:'卡片1'            },{                id: 2,                name:'卡片2'            },{                id: 3,                name:'卡片3'            },{                id: 4,                name:'卡片4'            },{                id: 5,                name:'卡片5'            }]            groupInfo.value = data;        }            // 获取卡⽚宽度,第⼀个参数是卡⽚个数,默认是整个数组,第⼆个参数是残余的margin        const getWidth = (num = groupInfo.value.length, restMargin = 16) => {            // 如果没有内容就返回0            if(!groupCardRef.value) return 0;            return num * (groupCardRef.value.offsetWidth + 16) - restMargin;        }        // 扭转滚动⽅向        const changeArrow = (scrollLeft) => {            // 默认获取scoll局部整个宽度            const getScrollWidth = getWidth();            // 获取残余宽度            const restWidth = getScrollWidth - scrollLeft            if (restWidth <= groupBoxRef.value.offsetWidth) {                direction.value = 'left'            } else if ( scrollLeft === 0 ) {                direction.value = 'right'            }        }        // ⿏标点击滚动        const groupScroll = async () => {            // 获取滚动宽度            const getMoveWidth = getWidth(3, 0);            if (direction.value === 'right') {                groupBoxRef.value.scrollLeft += getMoveWidth;            } else {                groupBoxRef.value.scrollLeft -= getMoveWidth;            }            // 滚动须要工夫能力获取最新的间隔            setTimeout(() => {                changeArrow(groupBoxRef.value.scrollLeft);            }, 500)        }        // 判断arrow是否展现        const checkArrowShow = () => {            arrowShow.value = getWidth() > groupBoxRef.value?.offsetWidth ? true : false;        }        watchEffect(() => {            checkArrowShow();        })        // 获取scroll函数的地位        const handleScroll = e => {            scrollPosition.left = e.target.scrollLeft;            scrollPosition.top = e.target.scrollTop;            changeArrow(scrollPosition.left);        }        getMyGroup();        onMounted(() => {            // 监听scroll事件            groupBoxRef.value.addEventListener('scroll', handleScroll, true);            // 监听窗⼝变动事件,判断arrow的展现            window.addEventListener('resize', checkArrowShow, true);            // 首次查看箭头展现            checkArrowShow();        })        return {            // data            groupInfo,            direction,            arrowShow,            // ref            groupBoxRef,            groupCardRef,            // methods            groupScroll        };    },});</script><style scoped>.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;}</style>