最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 实践积累:用Vue3简单写一个单行横向滚动组件

    正文概述 掘金(顽皮的雪狐七七)   2021-08-10   623

    目录

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

    • 效果图
    • 需求分析
    • 实现分析
      • 样式展示分析
      • 变量分析
      • 方法分析
    • 实现步骤
        1. 实现模板
        1. 实现css
        1. 首先获取list
        1. 页面挂载后监听groupBoxRef的scroll事件并获取当前的滚动位置
        1. 计算展示的宽度显隐箭头,当卡片宽度大于外层宽度就展示
        1. 控制箭头展示方向
        1. 监听外层宽度改变和窗口大小改变箭头显隐
    • 完整代码

    效果图

    把之前完成的一个效果图摘出来记录一下,核心代码在这里,如果项目中用到其他的技术,也很容易改。

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

    需求分析

    1. 展示数据始终一行,多余的部分可以出滚动条,同时隐藏滚动条样式。
    2. 支持笔记本触控滑动展示
    3. 支持鼠标点击滑动,多余的时候出现箭头按钮,默认滑动3个卡片的位置,顶头就切换方向
    4. 页面出现变动的时候要监听及时显示或隐藏按钮

    实现分析

    样式展示分析

    • 外层控制总体组件宽度
      • 内层箭头区域展示,内部使用flex布局,绝对定位到右侧
        • 内部箭头svg图标,垂直居中
      • 内层控制滚动区域宽度,内部使用flex布局,控制一层展示,溢出滚动展示,并隐藏滚动条
        • 内部确定卡片宽高和间距,最后一个右边距为0

    变量分析

    1. 卡片 list:Array
    2. 控制箭头显隐 arrowShow:Boolean,控制箭头方向 direction:String
    3. 获取滚动位置 scrollPosition = {left: 0, top: 0}
    4. 计算宽度需要的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和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
    • 滚动区域宽度:卡片数量 * (卡片宽度 + 右边距)- 最后一个右边距
    <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. 控制箭头展示方向

    • 初始朝右,横向滚动区域为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>
    

    起源地下载网 » 实践积累:用Vue3简单写一个单行横向滚动组件

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元