likes
comments
collection
share

Vue3 实现一个无缝滚动组件(支持鼠标手动滚动)

作者站长头像
站长
· 阅读数 14

Vue3 实现一个无缝滚动组件(支持鼠标手动滚动)

前言

在日常开发中,经常遇到需要支持列表循环滚动展示,特别是在数据化大屏开发中,无缝滚动使用频率更为频繁,在jquery时代,我们常用的无缝滚动组件为liMarquee,在vue中已经有vue-seamless-scroll组件(通过Vue2实现,不支持鼠标手动滚动),但是在使用过程中,发现滚动后会存在点击事件失效的问题,并且产品提了个需求,需要支持鼠标手动滚动,也要支持自动滚动,于是痛定思痛,决定通过Vue3来实现该功能,该组件已经实现上传npm,可以直接安装使用,链接在文尾。

实现

html部分

首先写一个基础的list结构,通过插槽接收外部传入的list数据,因为需要实现无缝滚动,需要复制出同一份的Dom,在最外层监听鼠标hover和leave的状态,以实现鼠标hover暂停滚动,绑定鼠标滚动事件,在鼠标滚动时记住滚动的位置,在恢复自动滚动时能从当前滚动位置继续滚动。

<div class="custom-list" ref="scrollBody" @mouseenter="mouseenterFunc" @mouseleave="mouseleaveFunc"
        @mousewheel="mousewheelFunc">
        <div class="list-body" :class="{
            'list-body2': isHorizontal
        }" ref="listBody" :style="{ transform: getScrollDistance() }">
            <slot></slot>
        </div>
        <div class="list-body" :class="{
            'list-body2': isHorizontal
        }" ref="tBody" v-if="isCanScroll" :style="{ transform: getScrollDistance() }">
            <slot></slot>
        </div>
</div>

实现逻辑

开始

通过父级传入的isHorizontal判断是横向滚动,还是垂直滚动

const start = () => {
    //判断是否可以滚动函数
    let isScrollFunc = (bodySize:number, listSize:number) => {
        if (bodySize > listSize) {
            scrollDistance.value = 0;
            isCanScroll.value = !1;
        }
    };

    isStop.value = !1;
    //判断是否可以滚动
    if (!isHorizontal.value) {
        isScrollFunc(bodyHeight.value, listHeight.value);
    } else {
        isScrollFunc(bodyWidth.value, listWidth.value);
    }
    if (isCanScroll.value) {
        run();
    }
}
开始滚动

计算目前滚动的距离,并判断需要滚动的方向,计算下一步滚动的距离。

const run = () => {
    //清空动画
    clearAnimation();
    animationFrame.value = window.requestAnimationFrame(() => {
        //滚动主逻辑函数
        let main = (listSize:number, bodySize:number) => {
            let tempScrollDistance = Math.abs(scrollDistance.value);
            if (scrollDistance.value < 0) {
                let cc = 2 * listSize - bodySize;
                if (tempScrollDistance > cc) {
                    scrollDistance.value = -(listSize - bodySize);
                }
            } else {
                scrollDistance.value = -listSize;
            }
        };

        //根据滚动方向判断使用高度或宽度控制效果
        if (!isHorizontal.value) {
            main(listHeight.value, bodyHeight.value);
        } else {
            main(listWidth.value, bodyWidth.value);
        }
        //判断滚动值
        if (!isStop.value) {
            if (
                props.scrollDirection === "top" ||
                props.scrollDirection === "left"
            ) {
                scrollDistance.value -= props.steep;
            } else if (
                props.scrollDirection === "bottom" ||
                props.scrollDirection === "right"
            ) {
                scrollDistance.value += props.steep;
            }
            run();
        }
    });
}
获取滚动样式

通过translate实现列表平移,已实现平滑滚动。

const getScrollDistance = () => {
    let style;
    if (!isHorizontal.value) {
        style = "translate(0px, " + scrollDistance.value + "px)";
    } else {
        style = "translate(" + scrollDistance.value + "px,0px)";
    }
    return style;
}
const clearAnimation = () => {
    if (animationFrame.value) {
        cancelAnimationFrame(animationFrame.value);
        animationFrame.value = null;
    }
}

鼠标滚动

鼠标滚动时实时计算滚动的距离,可通过传入的鼠标滚动速率来计算每次滚动多少。

const mousewheelFunc = (e:any) => {
    if (!isCanScroll.value || !props.isRoller) {
        return false;
    }
    let dis = e.deltaY;
    if (dis > 0) {
        scrollDistance.value -= props.rollerScrollDistance;
    } else {
        scrollDistance.value += props.rollerScrollDistance;
    }
    run();
}

使用

该组件已上传npm仓库,欢迎satrt使用

npm install @fcli/vue-auto-scroll --save-dev 来安装

在项目中使用
import VueAutoScroll from '@fcli/vue-auto-scroll';
const app=createApp(App)
app.use(VueAutoScroll);

使用示例:


<div class="content">
    <vue-auto-scroll :data="list" :steep="0.5" scrollDirection="top" :isRoller="true" :rollerScrollDistance="50">
      <div class="li" v-for="i in list" :key="i">
        {{ i }}
      </div>
    </vue-auto-scroll>
</div>

属性属性名称类型可选值
steep滚动的速率number为正数即可
scrollDirection滚动的方向stringtop ,bottom,left,right
isRoller是否可以使用滚轮滚动booleantrue,false
rollerScrollDistance滚轮滚动的速率number(isRoller 必须为 true)为正数即可
data接收异步数据array同步任务可不传

注: 当scrollDirection 为top或bottom时,一定要为 vue-auto-scroll 组件设置高度。 当scrollDirection 为left或right时,一定要为 vue-auto-scroll 组件设置宽度。并为嵌入vue-auto-scroll中的标签设置样式为display:inline-block; 示例样式名为list-item可以更改为其他类名。当scrollDirection 为left或right时,是基于行内元素的“white-space: nowrap;” 来控制强制不换行的。有可能会影响其内部的文字排列。可以在list-item 层添加 white-space: normal; 来处理给问题。并为其添加固定宽度,以保证文字可以正常换行及插件的正确计算与显示。如果没有为其添加固定宽度,会造成插件获取父容器层的宽度值错误,导致显示混乱

Vue3 实现一个无缝滚动组件(支持鼠标手动滚动)

git地址:gitee.com/fcli/vue-au…