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