# Vue Za wiper technical scheme

2022-04-29 13:07:03NEXT_ FE ## Preface

When I was doing business recently , The product students put forward such a demand ： After looking for open source, there is no suitable , So I decided to write a . General idea and main reference Swiper, monitor `touchmove` event , The dynamic change `translateX` To achieve sliding , Set a specific... When sliding to the boundary `translateX` Create a circular sliding effect . This paper mainly introduces the technical scheme of gesture sliding and button sliding , When reading articles, you can combine Warehouse code Read together . This article will be followed by this 8 A list of elements as an example , hypothesis Swiper Container Container visual elements are 4 individual , Explain ZSwiper Gesture sliding and button sliding functions .

## initialization

As shown in the figure above , To avoid sliding left to the far left or right to the far right , The sliding distance is too large , And when you can't see the elements , We need to copy an array and split it into two parts , Spliced in the original head and tail .

``````initDoubleList() {
const mid = Math.floor((this.list.length / 2));
this.halfLen = mid;
this.doubleList = [ ...this.list.slice(mid), ...this.list, ...this.list.slice(0, mid) ]
}
Copy code ``````

Then calculate the position of the left boundary , Will be the first 1 Elements , That's the index `4` The elements of , Put it on the far left of the container . Calculate the position of the right boundary , For later calculation .

``````initTranslateX() {
const translateX = this.getDomTranslateX() - this.itemFullWidth * this.halfLen;
this.setDomTranslateX(translateX);
return Math.abs(translateX);
},

mounted() {
this.initItemFullWidth();
const initialTranslateX = this.initTranslateX();
this.leftBorder = initialTranslateX;
this.rightBorder = this.itemFullWidth * this.list.length + initialTranslateX;
}
Copy code ``````

## Gestures slide left and right As shown in the figure above , The idea of gesture sliding is relatively simple , The main difficulty is when the slide reaches the boundary , How to make a cycle . Combined with the above 2 Look at the picture , For example, we keep sliding to the right , Slide to the right border （RightBorder） When , take `translateX` Return to the left boundary （LeftBorder）, The effect of scrolling to the right is infinite .

``````touchstart(evt) {
const touch = evt.targetTouches;

this.lastX = touch.pageX;
this.translateX = this.getDomTranslateX();
}

touchmove(evt) {
const touch = evt.targetTouches;
const xDiff = touch.pageX - this.lastX;

this.lastX = touch.pageX;
this.setMove(xDiff);
}

setMove(xDiff) {
this.translateX += xDiff;
const translateXAbs = Math.abs(this.translateX);

if (translateXAbs >= this.rightBorder) {
this.translateX = -(this.leftBorder + (translateXAbs - this.rightBorder));
} else if (translateXAbs <= this.leftBorder) {
this.translateX = -(this.rightBorder - (this.leftBorder - translateXAbs));
}

this.setDomTranslateX(this.translateX);
}
Copy code ``````

## Click the button to slide

### Click slide left

Next, click the left button to slide as an example to explain . Because the element may be truncated when you click the button , Product students want the truncated elements to be fully visible after the sliding stops , Disassemble the requirements and turn them into the following ： Let's look at the overall process first , As shown in the following code , The user clicks the button to trigger `handleSlide()`, Set the animation properties first , And then call `_slidePrev()` Refresh `translateX` Animation effect appears , Remove the animation attributes after the sliding animation .

``````mounted() {
//  Use  throttle  Avoid problems with animation caused by users' fast and continuous clicks
this.handleSlide = throttle(() => {
Object.assign(this.\$refs.swiperWrapper.style, {
'transition-duration': `\${ this.slideAnimationDuration }ms`,
});

this.translateX = this.getDomTranslateX();
this._slidePrev();

setTimeout(() => {
Object.assign(this.\$refs.swiperWrapper.style, {
'transition-duration': '0ms',
});
}, this.slideAnimationDuration);

}, this.slideAnimationDuration + 200, {
trailing: false,
});
},
methods: {
getObserveEntries() {
return new Promise((resolve) => {
const observer = new IntersectionObserver((entries) => {
observer.disconnect();

resolve(entries);
}, {
root: this.\$refs.swiperBody,
});

this.\$refs.swiperItems.forEach((el) => {
observer.observe(el);
});
})
},
_slidePrev() {
this.getObserveEntries().then(((entries) => {
const lastVisibleIndex = entries
.findLastIndex((item) => item.intersectionRatio >= this.intersectionRatioThreshold)

const isAllVisible = entries
.filter((item) => item.intersectionRatio >= this.intersectionRatioThreshold).length === this.halfLen

const targetIndex = isAllVisible ? lastVisibleIndex - 1 : lastVisibleIndex
const target = entries[targetIndex]

const xDiff = target.rootBounds.right - target.boundingClientRect.right
this.translateX += xDiff
this.setDomTranslateX(this.translateX)

const translateXAbs = Math.abs(this.translateX)
setTimeout(() => {
if (translateXAbs <= this.leftBorder) {
this.translateX = -(this.itemFullWidthValue * (targetIndex - this.halfLen + 1 + this.list.length))
this.setDomTranslateX(this.translateX)
}
}, this.slideAnimationDuration)
}))
},
}
Copy code ``````

So let's see `_slidePrev()` , utilize IntersectionObserver API We know that all four elements are just visible , Or is it not all visible . Calculate the displacement according to different conditions `xDiff`, Then compare with the current `translateX` Carry out operations , You can get the next position `translateX`. The red element in the figure represents the... In the code `target`, And the orange element means we want to end up in Swiper Container The leftmost element . because Swiper Container It's just right 4 Elements visible , So as long as `target` Move to the far right , The orange element must be on the far left .  The following figure shows after sliding , Entering the left boundary . At this time `targetIndex` yes `6`, To make a cycle , Our goal is to make the index `14` The element of appears in Swiper Container Far right . however `translateX` The positioning of is from Swiper Container From the far left , That is, if the index is `11` The brown element appears on the far left , The rightmost element over there must have an index of `14` The elements of . From this, we can deduce the following formula to calculate `finalTranslateX`

``````if (translateXOpt <= this.leftBorder) {
// targetIndex: 6
// halfLen: 4
// 6 + 1 - 4 + 8 = 11
this.translateX = -(this.itemFullWidth * (targetIndex + 1 - this.halfLen + this.list.length));
this.setDomTranslateX(this.translateX);
}
Copy code `````` There's a point to note , The boundary calculation should be put into `setTimeout` Medium wait `transition-duration` Only after being removed . Otherwise it will be because of `translateX` The process of changing from the right boundary to the left boundary is animated , Will appear 「 Turned a circle 」 Strange animation effects .

### Click and slide right

The calculation method of right slip is basically the same as that of left slip , Here are two pictures to show `xDiff`, The calculation method will not be expanded .  The following figure shows after sliding , Entering the right boundary . At this time `targetIndex` yes `12`, To make a cycle , Our goal is to make the index `4` The brown element appears in Swiper Container Leftmost , Move the length of an array forward . From this we can deduce the following formula ：

``````if (translateXAbs >= this.rightBorder) {
this.translateX = -(this.itemFullWidthValue * (targetIndex - this.list.length))
this.setDomTranslateX(this.translateX)
}
Copy code `````` End .