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