Take a look at the renderings first
The dropdown effect looks like a reference to Sina Weibo, scroll loading is ydui’s scroll loading component
I won’t bother here because the scroll load uses ydui components
Online experience point here
First of all, how is the pull down refresh implemented
1. When the page scrolls to the top, the user drags down with his finger. 2. When the user drags past the specified length and releases his finger, the page starts to bounce back and animation 4 is loaded. Execute the finished animation after loading is completeCopy the code
Realize the principle of
1. Determine if you scroll to the top, if not, do nothing. 2. Determine if the last drop-down refresh is over, and if not, block the browser's default behavior 3. If you scroll to the top and no pull-down refresh is in progress, record the position of event.touches[0]. Determine if the scroll reached the top, if not, do nothing. 2. Determine if the last drop-down refresh is over, if not, block the browser's default behavior 3. If the scroll reaches the top and no pull-down refresh is in progress 1. Determine whether the finger is sliding up or down, scroll up normally, and perform pull-down refresh 2. If the finger is pulled down, judge whether the sliding distance exceeds the specified distance, and if so, change the animation effect (refresh by drop-down -> release refresh). Third, the touchend event is the same as the judgment in touchMove, the only difference is whether the sliding distance exceeds the specified distance to trigger the callback and execute the animation in the refreshCopy the code
Gets the page scroll position (or div internal scroll position)
@method getScrollTop @param {dom} DOM node @return {Number} scroll position */
function getScrollTop(element) {
if (element === window) {
return Math.max(
window.pageYOffset || 0.document.documentElement.scrollTop
)
} else {
return element.scrollTop
}
}
Copy the code
The event processing
/ / touchstart event
function touchStartHandler(event) {
// Returns if a pull-down refresh is being performed
if (this.touches.loading) {
event.preventDefault()
return
}
// When you scroll down, return directly
GetScrollTop (this.scrollView) getScrollTop(this.scrollView
/ / this. $refs. DragBox. GetBoundingClientRect (). The top of elements from the window at the top of the distance
/ / this. OffsetTop initialization for the page when this. $refs. DragBox. GetBoundingClientRect (). The top value
if (
this.getScrollTop(this.scrollview) > 0 ||
this.$refs.dragBox.getBoundingClientRect().top < this.offsetTop
) {
return
}
// Data initialization
this.touches.loading = false // Whether the refresh callback is in the pull-down
this.touches.startClientY = 0 // Touch the initial position
this.touches.isDraging = false // Whether to start pull-down refresh
this.touches.statusText = 'Pull refresh' // Drop down to refresh the description text in the animation
this.moveOffset = 0 // The distance of the finger slide
// Record the touch position
this.touches.startClientY = event.touches[0].clientY
}
/ / touchmove events
function touchMoveHandler(event) {
const touches = this.touches
// Record the current touch position, in order to compare with the next touch position, determine whether to move up or down
touches.currentClientY = event.touches[0].clientY
// When you scroll down, return directly
if (
this.getScrollTop(this.scrollview) > 0 ||
this.$refs.dragBox.getBoundingClientRect().top < this.offsetTop
) {
this.touches.isDraging = false // No pull-down refresh started
this.moveOffset = 0 // The finger slide distance is 0
return
}
// Returns if a pull-down refresh is being performed
if (this.touches.loading) {
event.preventDefault()
return
}
// The current position of the touch
const currentY = event.touches[0].clientY
// Prevent your fingers from sliding down and not scrolling properly
if(! touches.isDraging && currentY < touches.startClientY) {return
}
// Pull the finger down and then slide it up to indicate that the finger is already above the touch position
if (
touches.isDraging &&
(currentY - touches.startClientY < 0 ||
this.$refs.dragBox.getBoundingClientRect().top <
this.offsetTop)
) {
// this.isDragToUp = true;
event.preventDefault()
return
}
// Slide your finger down
if (touches.isDraging && this.getScrollTop(this.scrollview) === 0) {
event.preventDefault()
}
// // start drop-down refresh
this.touches.isDraging = true
// The distance of the finger slide
let deltaSlide = currentY - touches.startClientY
// If the specified distance is exceeded, the condition for releasing the update is reached
// Touches. Distance indicates the height of the loading animation at the top
//this.double is a multiple of how far the finger moves and how far the page actually moves
if (deltaSlide >= touches.distance * this.double) {
this.touches.statusText = 'Release update'
} else {
this.touches.statusText = 'Pull refresh'
}
// Record the sliding distance
this.moveOffset = deltaSlide
}
/ / touchend events
function touchEndHandler(event) {
const touches = this.touches
// Returns if a pull-down refresh is being performed
if (this.touches.loading) {
event.preventDefault()
return
}
// When you scroll down, return directly
if (
this.getScrollTop(this.scrollview) > 0 ||
this.$refs.dragBox.getBoundingClientRect().top < this.offsetTop
) {
this.touches.isDraging = false
this.moveOffset = 0
return
}
const currentY = event.changedTouches[0].clientY
// Indicates that the finger is already above the touch position
if (
currentY - touches.startClientY < 0 ||
this.$refs.dragBox.getBoundingClientRect().top < this.offsetTop
) {
this.touches.isDraging = false
event.preventDefault()
return
}
// Pull-down refresh prevents default browser behavior
if (this.getScrollTop(this.scrollview) === 0) {
event.preventDefault()
}
// The distance of the finger slide
let deltaSlide = currentY - touches.startClientY
// If the specified distance is exceeded
if (deltaSlide >= touches.distance * this.double) {
// Update the callback and animation changes, this part of the source code, patient look at the source code, or can easily see understand
some code...
} else {
this.touches.isDraging = false
// Do not refresh if the distance is not enough
this.Retract(0.false)}}Copy the code
Only the main parts are shown above, and the full source code is at the end of the article. Then I packaged the pull-down refresh and pull-up load as a way to use vUE components
/ / introduction of the import YdInfinitescroll from '. / components/InfiniteScroll. Vue '/ register/components: <yd-infinitescroll :callback="callback" :callback="callback" ref="infinitescrollDemo" > <div slot='list'> ... </div> </yd-infinitescroll>Copy the code
Description:
1. Incoming callback parameter open rolling load function 1. Enclosing $refs. InfinitescrollDemo. $emit (' ydui. Infinitescroll. FinishLoad) says a single data request is completed 2. This. $refs. InfinitescrollDemo. $emit (' ydui. Infinitescroll. LoadedDone) said all data request is completed 3. This. $refs. InfinitescrollDemo. $emit (' ydui. Infinitescroll. ReInit) said the re-initialization 2. Incoming pullcallback parameter to open the drop-down to refresh the function 1. The update is successful, please call this. $refs. InfinitescrollDemo. $emit (ydui. Pullrefresh. FinishLoad. 'success', true) parameters False Disables the prompt. The default value is true 2. The update is successful, please call this. $refs. InfinitescrollDemo. $emit (' ydui. Pullrefresh. FinishLoad. Fail, true) parameter true began to tip, false closed tips, true 3 by default. Pass the pullTipBgColor parameter to change the background color for the successful pullrefresh state. The default color is blue (# 171dCA).Copy the code
Component source code, directly copy source code saved as InfiniteScroll. Vue can start to use
<template>
<div>
<! -- Drop down to refresh -->
<div
class="dragBox"
ref="dragBox"
:style="{ transform: `translateY(${moveOffset / double}px)` }"
>
<! Drop down refresh animation effect -->
<div
class="yd-pullTip"
:style="{ height: `${moveOffset / double}px`, top: `-${moveOffset / double}px`, paddingBottom: moveOffset / double > (touches.distance - 20) / 2 ? (touches.distance - 20) / 2 + 'px' : `${moveOffset / double}px` }"
>
<img
v-if="touches.loading"
src="data:image/gif;base64,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"
/>
<img
v-else
:class="{ rotate: moveOffset >= touches.distance * double }"
src="data:image/png; base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAA4CAYAAAB3yEEBAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvb S5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iY WRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gP HJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvd XQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tL yIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvd G9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OEFFN0NBNENDQkVEMTFFOTg3ODBCRjE0Q0NBQTdFMDEiIHhtcE1NOkRvY 3VtZW50SUQ9InhtcC5kaWQ6OEFFN0NBNERDQkVEMTFFOTg3ODBCRjE0Q0NBQTdFMDEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ie G1wLmlpZDo4QUU3Q0E0QUNCRUQxMUU5ODc4MEJGMTRDQ0FBN0UwMSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4QUU3Q0E0QkNCRUQxMUU5ODc4MEJGM TRDQ0FBN0UwMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pk84QwMAAALZSURBVHja7 Jm7jtpAFIbtgeXiBhvIG6RaEFRcnmIfIc02W20RVtr0KZIiaTYNTV6CJq/ApYpAW21DQwei4SIhQeZf+aDRrD0e20iJlBkJgeZ2vvnPmcN4bK9WKytluRF+D 9JMlE0zeLvd3s1ms956vX7ved5zvV5/VywWf8r9bNu2TqdT5Hx2HGXESQEyHo+/73Y7h9odx1m32+2HpEBMB4CKCsSvd0ej0ROvv5Xn0VEmEkaeJAyECup5+ 7cgoNQwMghf+RkkbLVQaDKZxAZiUa4RVnwLRfb7vaPqR2Wz2bhxFWK6rvFjwdF1Z1QMJXITKaICUe2WODHEomJkOBw+BcVILpc7yP0zmYyVz+cPSRViKpCwG OF5ZNtqtT69mYwxq9vt3hcKhW0ShZjKNSIIFYDwxNbjCW4RNBb1nU6nFwQEhVRALGT7vroGbhFdAwM+SF8lN9oBRC4T51ABMVH+oIQG6UkRGIAhnWyKfuQyO QUQEOyJIcCIXAZBHe0Qcg3/7kfllyCFwlzGE+MXKER2GMWImFnFrSrESF+GiIJCuwwkjiGFeGx+IDfd8GPAx4hg7esmuqB20WXyGC6AO51OH8Hxqgw/RlzLq /Rj5J5AdOJEVXCsCHIZ5l0ul9fnAC6VSnN51wAk6FySpoTFEOwTzKDRaHzlJ7U5dk61Wn2GpDKIbtDq7jLYgb1yufwC++DICh0W3OCBK3SV9iyr4zIeiwvf1 vnsnMWKj8cjVj4Qt7P8x6d7jtU9uvLPrzdJjwzLOyCsPm1RzcOSDrwkEBaNT9b6C0XeDATHrH+oGBgDY2AMjIExMAbGwBgYA2NgDMz/BJPk1iI2DD394cZC9 dia5NGYJVmxf5NwFed5+iLvm8IMMcYO5Ar6xuWPf8dzho7jtsQxAxjXdefiqj3P+w3FyHBcV6UJ4EGz2fxcqVRe8AID12K1Wu2HleLWy477KjngButir5L/C DAAX5Ad05wlsggAAAAASUVORK5CYII="
/>
{{ touches.statusText }}
</div>
<! Pull down refresh prompt effect -->
<div class="yd-Tip" v-if="pullupdateStatus">
{{ pullupdateText }}
<span
:style="PullupdateText === 'Update successful'? `backgroundColor: ${pullTipBgColor}; ` : 'backgroundColor: #aeaeae; '"
></span>
</div>
<slot name="list"></slot>
</div>
<div ref="tag" style="height: 0;"></div>
<div class="yd-list-loading" v-if=! "" isDone">
<div v-show="isLoading">
<slot name="loadingTip">
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid"
class="lds-ellipsis"
>
<circle cx="84" cy="50" r="5.04711" fill="#f3b72e">
<animate
attributeName="r"
values="10; 0; 0; 0; 0"
keyTimes="0; 0.25; 0.5; 0.75; 1"
keySplines="0 0.5 0.5 1; 0.5 0.5 1 0; 0.5 0.5 1 0; 0.5 0.5 0 1"
calcMode="spline"
dur="1.7 s"
repeatCount="indefinite"
begin="0s"
></animate>
<animate
attributeName="cx"
values="84; 84; 84; 84; 84"
keyTimes="0; 0.25; 0.5; 0.75; 1"
keySplines="0 0.5 0.5 1; 0.5 0.5 1 0; 0.5 0.5 1 0; 0.5 0.5 0 1"
calcMode="spline"
dur="1.7 s"
repeatCount="indefinite"
begin="0s"
></animate>
</circle>
<circle cx="66.8398" cy="50" r="10" fill="#E8574E">
<animate
attributeName="r"
values="0; 10; 10; 10; 0"
keyTimes="0; 0.25; 0.5; 0.75; 1"
keySplines="0 0.5 0.5 1; 0.5 0.5 1 0; 0.5 0.5 1 0; 0.5 0.5 0 1"
calcMode="spline"
dur="1.7 s"
repeatCount="indefinite"
begin="-0.85s"
></animate>
<animate
attributeName="cx"
values="16; 16. 50; 84; 84"
keyTimes="0; 0.25; 0.5; 0.75; 1"
keySplines="0 0.5 0.5 1; 0.5 0.5 1 0; 0.5 0.5 1 0; 0.5 0.5 0 1"
calcMode="spline"
dur="1.7 s"
repeatCount="indefinite"
begin="-0.85s"
></animate>
</circle>
<circle cx="32.8398" cy="50" r="10" fill="#43A976">
<animate
attributeName="r"
values="0; 10; 10; 10; 0"
keyTimes="0; 0.25; 0.5; 0.75; 1"
keySplines="0 0.5 0.5 1; 0.5 0.5 1 0; 0.5 0.5 1 0; 0.5 0.5 0 1"
calcMode="spline"
dur="1.7 s"
repeatCount="indefinite"
begin="-0.425s"
></animate>
<animate
attributeName="cx"
values="16; 16. 50; 84; 84"
keyTimes="0; 0.25; 0.5; 0.75; 1"
keySplines="0 0.5 0.5 1; 0.5 0.5 1 0; 0.5 0.5 1 0; 0.5 0.5 0 1"
calcMode="spline"
dur="1.7 s"
repeatCount="indefinite"
begin="-0.425s"
></animate>
</circle>
<circle cx="16" cy="50" r="4.95289" fill="# 304153">
<animate
attributeName="r"
values="0; 10; 10; 10; 0"
keyTimes="0; 0.25; 0.5; 0.75; 1"
keySplines="0 0.5 0.5 1; 0.5 0.5 1 0; 0.5 0.5 1 0; 0.5 0.5 0 1"
calcMode="spline"
dur="1.7 s"
repeatCount="indefinite"
begin="0s"
></animate>
<animate
attributeName="cx"
values="16; 16. 50; 84; 84"
keyTimes="0; 0.25; 0.5; 0.75; 1"
keySplines="0 0.5 0.5 1; 0.5 0.5 1 0; 0.5 0.5 1 0; 0.5 0.5 0 1"
calcMode="spline"
dur="1.7 s"
repeatCount="indefinite"
begin="0s"
></animate>
</circle>
<circle cx="16" cy="50" r="0" fill="#f3b72e">
<animate
attributeName="r"
values="0; 0; 10; 10; 10"
keyTimes="0; 0.25; 0.5; 0.75; 1"
keySplines="0 0.5 0.5 1; 0.5 0.5 1 0; 0.5 0.5 1 0; 0.5 0.5 0 1"
calcMode="spline"
dur="1.7 s"
repeatCount="indefinite"
begin="0s"
></animate>
<animate
attributeName="cx"
values="16; 16. 16. 50; 84"
keyTimes="0; 0.25; 0.5; 0.75; 1"
keySplines="0 0.5 0.5 1; 0.5 0.5 1 0; 0.5 0.5 1 0; 0.5 0.5 0 1"
calcMode="spline"
dur="1.7 s"
repeatCount="indefinite"
begin="0s"
></animate>
</circle>
</svg>
</template>
</slot>
</div>
</div>
<div class="yd-list-donetip" v-show=! "" isLoading && isDone">
<slot name="doneTip">There's no more data</slot>
</div>
</div>
</template>
<script type="text/babel">
export default {
name: 'yd-infinitescroll',
data() {
return {
isLoading: false.isDone: false.num: 1.touches: {
loading: false.// Whether the refresh callback is in the pull-down
distance: 60.// Release the refresh when the slide distance is greater than 100
startClientY: 0.currentClientY: Math.pow(2.32), // The current touch position
isDraging: false.// Whether to start pull-down refresh
statusText: 'Pull refresh' // State description at this time
},
moveOffset: 0.// Pull down the length of the finger to scroll
double: 3.// Multiples of the sliding distance and the pull-down distance
// Step: 20 // Release the finger interface to slide up speed
time: 100.pullupdateStatus: false.// Whether to display the updated prompt after the drop-down refresh
pullupdateText: 'Update successful' // Display a drop-down refresh prompt after the update}},props: {
callback: {
type: Function
},
pullcallback: {
type: Function
},
distance: {
default: 0,
validator(val) {
return /^\d*$/.test(val)
}
},
scrollTop: {
type: Boolean.default: true
},
pullTipBgColor: {
type: String.default: '#171dca'}},methods: {
init() {
if (this.scrollTop) {
if (this.scrollview === window) {
window.scrollTo(0.0)}else {
this.scrollview.scrollTop = 0}}this.scrollview.addEventListener(
'scroll'.this.throttledCheck,
false
)
this.$on('ydui.infinitescroll.loadedDone', () = > {this.isLoading = false
this.isDone = true
})
this.$on('ydui.infinitescroll.finishLoad', () = > {this.isLoading = false
})
this.$on('ydui.infinitescroll.reInit', () = > {this.isLoading = false
this.isDone = false
})
},
pullinit() {
const dragBox = this.$refs.dragBox
dragBox.addEventListener('touchstart'.this.touchStartHandler)
dragBox.addEventListener('touchmove'.this.touchMoveHandler)
dragBox.addEventListener('touchend'.this.touchEndHandler)
// Prevent the wechat browser from pulling down the domain name
document.body.addEventListener('touchmove'.this.stopDragEvent, {
passive: false // The call blocks the default behavior
})
// The distance between the container and the top
this.offsetTop = this.$refs.dragBox.getBoundingClientRect().top
// The pull-up load is complete
this.$on('ydui.pullrefresh.finishLoad.success', (tip = true) = > {this.pullupdateText = 'Update successful'
this.Retract(0, tip)
})
this.$on('ydui.pullrefresh.finishLoad.fail', (tip = true) = > {this.pullupdateText = 'Update failed'
this.Retract(0, tip)
})
},
scrollHandler() {
if (this.isLoading || this.isDone) return
const scrollview = this.scrollview
const contentHeight = document.body.offsetHeight
const isWindow = scrollview === window
const offsetTop = isWindow
? 0
: scrollview.getBoundingClientRect().top
const scrollviewHeight = isWindow
? contentHeight
: scrollview.offsetHeight
if(! scrollview) {// eslint-disable-next-line
console.warn("Can't find the scrollview!")
return
}
if (!this.$refs.tag) {
// eslint-disable-next-line
console.warn("Can't find the refs.tag!")
return
}
const tagOffsetTop =
Math.floor(this.$refs.tag.getBoundingClientRect().top) - 1
const distance =
!!this.distance && this.distance > 0? ~ ~this.distance
: Math.floor(contentHeight / 10)
if (
tagOffsetTop > offsetTop &&
tagOffsetTop - (distance + offsetTop) * this.num <=
contentHeight &&
this.$el.offsetHeight > scrollviewHeight
) {
this.isLoading = true
this.callback && this.callback()
this.num++
}
},
throttle(method, context) {
clearTimeout(method.tId)
method.tId = setTimeout((a)= > {
method.call(context)
}, 30)
},
throttledCheck() {
this.throttle(this.scrollHandler)
},
getScrollview(el) {
let currentNode = el
while( currentNode && currentNode.tagName ! = ='HTML'&& currentNode.tagName ! = ='BODY' &&
currentNode.nodeType === 1
) {
let overflowY = document.defaultView.getComputedStyle(
currentNode
).overflowY
if (overflowY === 'scroll' || overflowY === 'auto') {
return currentNode
}
currentNode = currentNode.parentNode
}
return window
},
@method getScrollTop @return {Number} scroll position */
getScrollTop(element) {
if (element === window) {
return Math.max(
window.pageYOffset || 0.document.documentElement.scrollTop
)
} else {
return element.scrollTop
}
},
touchStartHandler(event) {
// Returns if a pull-down refresh is being performed
if (this.touches.loading) {
event.preventDefault()
return
}
// When you scroll down, return directly
if (
this.getScrollTop(this.scrollview) > 0 ||
this.$refs.dragBox.getBoundingClientRect().top < this.offsetTop
) {
return
}
// Data initialization
this.touches.loading = false
this.touches.startClientY = 0
this.touches.isDraging = false
this.touches.statusText = 'Pull refresh'
this.moveOffset = 0
// Record the touch position
// this.touches.startClientX = event.touches[0].clientX
this.touches.startClientY = event.touches[0].clientY
},
touchMoveHandler(event) {
const touches = this.touches
// Record the current touch position
touches.currentClientY = event.touches[0].clientY
// When you scroll down, return directly
if (
this.getScrollTop(this.scrollview) > 0 ||
this.$refs.dragBox.getBoundingClientRect().top < this.offsetTop
) {
// this.dragTip.translate = 0;
// this.resetParams();
this.touches.isDraging = false
this.moveOffset = 0
return
}
// Returns if a pull-down refresh is being performed
if (this.touches.loading) {
event.preventDefault()
return
}
// console.log(this.getScrollTop(this.scrollview))
// console.log(' executed ')
const currentY = event.touches[0].clientY
// const currentX = event.touches[0].clientX
// Prevent your fingers from sliding down and not scrolling properly
if(! touches.isDraging && currentY < touches.startClientY) {return
}
// Pull the finger down and then slide it up to indicate that the finger is already above the touch position
if (
touches.isDraging &&
(currentY - touches.startClientY < 0 ||
this.$refs.dragBox.getBoundingClientRect().top <
this.offsetTop)
) {
// this.isDragToUp = true;
event.preventDefault()
return
}
// Slide your finger down
if (touches.isDraging && this.getScrollTop(this.scrollview) === 0) {
event.preventDefault()
}
// // start drop-down refresh
this.touches.isDraging = true
// const touchAngle =
// (Math.atan2(
// Math.abs(currentY - touches.startClientY),
// Math.abs(currentX - touches.startClientX)
/ /) *
/ / / 180)
// Math.PI
// if (90 - touchAngle > 45) return
// The distance of the finger slide
let deltaSlide = currentY - touches.startClientY
// If the specified distance is exceeded, the condition for releasing the update is reached
if (deltaSlide >= touches.distance * this.double) {
this.touches.statusText = 'Release update'
} else {
this.touches.statusText = 'Pull refresh'
}
// Record the sliding position
this.moveOffset = deltaSlide
// console.log(this.moveOffset)
},
touchEndHandler(event) {
const touches = this.touches
// console.log(this.touches.isDraging)
// Returns if a pull-down refresh is being performed
if (this.touches.loading) {
event.preventDefault()
return
}
// When you scroll down, return directly
if (
this.getScrollTop(this.scrollview) > 0 ||
this.$refs.dragBox.getBoundingClientRect().top < this.offsetTop
) {
this.touches.isDraging = false
this.moveOffset = 0
return
}
const currentY = event.changedTouches[0].clientY
// const currentX = event.changedTouches[0].clientX
// Indicates that the finger is already above the touch position
if (
currentY - touches.startClientY < 0 ||
this.$refs.dragBox.getBoundingClientRect().top < this.offsetTop
) {
this.touches.isDraging = false
event.preventDefault()
return
}
// Pull-down refresh prevents default browser behavior
if (this.getScrollTop(this.scrollview) === 0) {
event.preventDefault()
}
// The distance of the finger slide
let deltaSlide = currentY - touches.startClientY
// If the specified distance is exceeded
if (deltaSlide >= touches.distance * this.double) {
// Update the animation
this.touches.statusText = 'Loading'
// alert(' drop refresh ')
this.touches.startClientY = 0
this.touches.isDraging = false
this.Retract(touches.distance * this.double)
return
} else {
this.touches.isDraging = false
// Do not refresh if the distance is not enough
this.Retract(0.false)
}
},
stopDragEvent(event) {
this.touches.isDraging && event.preventDefault()
},
Retract(offsetTop, tip) {
let timer = setInterval((a)= > {
// Calculate the distance of each movement according to the time
// Total time/Time per exercise = times of exercise
// Total length/movement times = each movement distance
let step = (
(this.touches.distance * this.double) /
((this.time * 60) / 1000).toFixed(2)
).toFixed(2)
if (this.moveOffset - step > offsetTop) {
this.moveOffset -= step
} else {
this.moveOffset = offsetTop
clearInterval(timer)
if(offsetTop ! = =0) {
this.touches.loading = true
this.pullcallback && this.pullcallback()
} else {
/ / reset
this.touches.loading = false
this.touches.startClientY = 0
this.touches.isDraging = false
this.touches.statusText = 'Pull refresh'
// Perform loading animation
if (tip) {
// Execute the update success or failure animation
this.pullupdateStatus = true
setTimeout((a)= > {
this.pullupdateStatus = false
}, 1000)}}}},1000 / 60)
}
},
mounted() {
this.scrollview = this.getScrollview(this.$el)
if (this.callback) {
this.init()
}
if (this.pullcallback) {
this.pullinit()
}
},
beforeDestroy() {
this.scrollview.removeEventListener('scroll'.this.throttledCheck)
this.$refs.dragBox.removeEventListener(
'touchstart'.this.touchStartHandler
)
this.$refs.dragBox.removeEventListener(
'touchmove'.this.touchMoveHandler
)
this.$refs.dragBox.removeEventListener('touchend'.this.touchEndHandler)
document.body.removeEventListener('touchmove'.this.stopDragEvent)
}
}
</script>
<style lang="scss" scoped>@keyframes intact { 0% { border-radius: 50%; } 100% { border-radius: 0%; }}. Yd {&-list-loading {padding: 0.1rem 0; text-align: center; The font - size: 0.26 rem; color: #999; Height: 0.66 rem; box-sizing: content-box; & - box {height: 0.66 rem; overflow: hidden; The line - height: 0.66 rem; } img {height: 0.66rem; display: inline-block; } SVG {width: 0.66rem; Height: 0.66 rem; }} &-list-donetip {font-size: 0.24rem; text-align: center; Padding: 0.25 rem 0; color: #777; } &-pullTip { text-align: center; The font - size: 0.24 rem; position: absolute; left: 0; right: 0; background: #eeeeee; color: #a5a5a5; overflow: hidden; display: flex; align-items: flex-end; justify-content: center; img { height: 20px; margin-right: 12px; margin-bottom: -1px; The transition: 0.1 s linear transform; transform: rotate(180deg); } img.rotate { transform: rotate(0deg); } } &-Tip { z-index: 99999999; position: absolute; left: 0; right: 0; top: 0; height: 30px; line-height: 30px; The font - size: 0.24 rem; overflow: hidden; text-align: center; color: #fff; span { position: absolute; top: 0; left: 0; z-index: -1; display: block; width: 100%; padding-top: 100%; border-radius: 50%; Animation: intact 0.1s Linear forwards; }}}</style>
Copy the code