Vue3 implements a seamless scrolling component (supports manual mouse scrolling)

Vue3 implements a seamless scrolling component (supports manual mouse scrolling)

Foreword

In daily development, we often encounter the need to support list loop scrolling display. Especially in data-based large-screen development, seamless scrolling is used more frequently. In the jquery era, our commonly used seamless scrolling component is liMarquee. In vue There is already a vue-seamless-scroll component (implemented through Vue2, which does not support manual mouse scrolling). However, during use, it was found that there would be a problem with click events failing after scrolling, and the product put forward a requirement to support manual mouse scrolling. , also need to support automatic scrolling, so I learned from the pain and decided to implement this function through Vue3. This component has been uploaded to npm and can be installed and used directly. The link is at the end of the article.

Implementation

html part

First, write a basic list structure to receive external incoming list data through slots. Because seamless scrolling needs to be achieved, the same Dom needs to be copied, and the status of mouse hover and leave is monitored on the outermost layer to realize mouse hover. Pause scrolling, bind the mouse scroll event, remember the scrolling position when the mouse scrolls, and continue scrolling from the current scrolling position when resuming automatic scrolling.

<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>
Implement logic
Start

Determine whether it is horizontal scrolling or vertical scrolling through the isHorizontal passed in by the parent

const start = () => {<!-- -->
    //Determine whether the scroll function can be
    let isScrollFunc = (bodySize:number, listSize:number) => {<!-- -->
        if (bodySize > listSize) {<!-- -->
            scrollDistance.value = 0;
            isCanScroll.value = !1;
        }
    };

    isStop.value = !1;
    //Determine whether scrolling is possible
    if (!isHorizontal.value) {<!-- -->
        isScrollFunc(bodyHeight.value, listHeight.value);
    } else {<!-- -->
        isScrollFunc(bodyWidth.value, listWidth.value);
    }
    if (isCanScroll.value) {<!-- -->
        run();
    }
}
Start scrolling

Calculate the current scrolling distance, determine the required scrolling direction, and calculate the next scrolling distance.

const run = () => {
    //clear animation
    clearAnimation();
    animationFrame.value = window.requestAnimationFrame(() => {
        //Scrolling main logic function
        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;
            }
        };

        //Determine the effect of using height or width control based on the scroll direction
        if (!isHorizontal.value) {
            main(listHeight.value, bodyHeight.value);
        } else {
            main(listWidth.value, bodyWidth.value);
        }
        //Determine the scroll 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();
        }
    });
}
Get scrolling style

List panning is achieved through translate, and smooth scrolling has been achieved.

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

Mouse scroll

The scrolling distance is calculated in real time when the mouse is scrolling. The incoming mouse scrolling rate can be used to calculate how much each scroll is.

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();
}
Use

This component has been uploaded to the npm warehouse, and satrt is welcome to use it.

npm install @fcli/vue-auto-scroll --save-dev to install

Use in projects
import VueAutoScroll from '@fcli/vue-auto-scroll';
const app=createApp(App)
app.use(VueAutoScroll);

Usage example:

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

Attribute Attribute name Type Optional value
steep The scrolling rate number can be a positive number
scrollDirection Scrolling direction string top,bottom,left,right
isRoller Whether it is possible to use the wheel to scroll boolean true,false
rollerScrollDistance The rolling speed of the wheel number (isRoller must be true) can be a positive number
data Receive asynchronous data array Synchronization tasks do not need to be passed

Note: When scrollDirection is top or bottom, be sure to set the height for the vue-auto-scroll component. When scrollDirection is left or right, be sure to set the width for the vue-auto-scroll component. And set the style for the label embedded in vue-auto-scroll to display:inline-block; the example style name is list-item and can be changed to other class names. When scrollDirection is left or right, the forced non-wrap is controlled based on the “white-space: nowrap;” of the inline element. It may affect its internal text arrangement. You can add white-space: normal; at the list-item layer to solve the problem. And add a fixed width to it to ensure that the text can wrap normally and the plug-in can be calculated and displayed correctly. If a fixed width is not added to it, it will cause the plug-in to obtain the width value of the parent container layer incorrectly, resulting in confusing display.

git address: https://gitee.com/fcli/vue-auto-scroll.git