头图

content

Copyright statement: This article is the original article of the blogger and follows the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this statement for reprinting.

  • Effect picture
  • demand analysis
  • Realization analysis

    • Style display analysis
    • Variable analysis
    • Method analysis
  • Implementation steps

      1. Implementation template
      1. Implement css
      1. First get the list
      1. After the page is mounted, listen to the scroll event of groupBoxRef and get the current scroll position
      1. Calculate the width of the display to show the hidden arrow, when the width of the card is greater than the width of the outer layer, it will be displayed
      1. Control arrow display direction
      1. Monitor the outer width change and the window size change, the arrow shows and hides
  • Complete code

Effect picture

Take out a previously completed rendering and record it. The core code is here. If other technologies are used in the project, it is easy to change.

demand analysis

  1. The display data is always one line, the extra part can be scrolled out, and the scroll bar style is hidden at the same time.
  2. Support notebook touch sliding display
  3. Support mouse click and slide, arrow buttons appear when redundant, the default position of sliding 3 cards, the head will switch direction
  4. When the page changes, you must monitor to show or hide the button in time

Realization analysis

Style display analysis

  • The outer layer controls the overall component width

    • The inner arrow area is displayed, and the flex layout is used inside, which is absolutely positioned to the right

      • Internal arrow svg icon, vertically centered
    • The inner layer controls the width of the scrolling area, uses flex layout inside, controls the display of one layer, overflows the scroll display, and hides the scroll bar

      • Determine the card width, height and spacing internally, and the last right margin is 0

    Variable analysis

  • Card list:Array
  • The control arrow is arrowShow:Boolean and hidden 06110e79b2f3b3, and the direction of the control arrow is direction:String
  • Get the scroll position scrollPosition = {left: 0, top: 0}
  • Ref required to calculate the width: control the scroll bar layer groupBoxRef , card groupCardRef

Method analysis

  1. Get the list (can be http or props, depending on needs)
  2. After the page is mounted, listen to the scroll event of groupBoxRef and the resize event of window changes
  3. The method of judging whether the arrow is visible or hidden, and the method of changing the direction of the arrow
  4. How to click the arrow with the mouse

Implementation steps

1. Implementation template

<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. Implement 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. First get the 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. After the page is mounted, listen to the scroll event of groupBoxRef and get the current scroll position

// 添加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. Calculate the width of the display to show the hidden arrow, when the width of the card is greater than the width of the outer layer, it will be displayed

  • Card width: groupCardRef.value.offsetWidth
  • Outer layer width: groupBoxRef.value.offsetWidth
  • Scroll area width: Number of cards * (card width + right margin)-the last right margin
<div class="scrollX" v-if="arrowShow">
    <img src='../assets/arrow-left-bold.svg'/>
</div>
...
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 = getWidth() > groupBoxRef.value?.offsetWidth ? true : false;
}
...
onMounted(() => {
    // 监听scroll事件
    groupBoxRef.value.addEventListener('scroll', handleScroll, true);
    // 首次检查箭头展示
    checkArrowShow();
})

6. Control arrow display direction

  • right, the 16110e79b2f82a horizontal scrolling area is 0 when it is right, and the remaining width is smaller than the outer width when it is left
  • Remaining width: scroll area width-scroll distance
<!-- 添加点击箭头事件和箭头方向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. Monitor the outer width change and the window size change, and the arrow is displayed and hidden

import { defineComponent, ref, reactive, onMounted, watchEffect } from 'vue';
...
watchEffect(() => {
    checkArrowShow();
})

onMounted(() => {
    ...
    // 监听窗⼝变化事件,判断arrow的展示
    window.addEventListener('resize', checkArrowShow, true);
})

Complete code

<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>

顽皮的雪狐七七
787 声望74 粉丝

学习探索使我内心平静,