scroller.vue 3.3 KB
<template>
    <div class="scroller-box">
        <div
            class="scroller-content"
            ref="scroller"
            :style="scrollerStyle"
            @touchstart="touchstart"
            @touchmove="touchmove"
            @touchend="touchend">
            <div class="scroller-top" :style="styleTop"></div>
            <slot></slot>
        </div>
    </div>
</template>

<script>
export default {
    name: 'Scroller',
    props: {
        topDistance: {
            type: Number,
            default: 50
        }
    },
    data() {
        return {
            distance: 0,
            transition: false,
            direction: '',
            isLoading: false,
            styleTop: {}
        };
    },
    computed: {
        scrollerStyle() {
            const style = {};

            if (this.distance) {
                style.transform = `translate(0px, ${this.distance}px)`;
            }
            if (this.transition) {
                style.transition = 'transform .2s ease';
            }
            return style;
        }
    },
    methods: {
        loading() {
            this.isLoading = true;
        },
        stop() {
            setTimeout(() => {
                this.isLoading = false;
                this.styleTop = {
                    transition: 'margin .2s ease'
                };
            }, 500);
        },
        touchstart(e) {
            this.startY = e.touches[0].clientY;
            if (this.$el.scrollTop === 0) {
                this.transition = false;
                this.topStatus = 'pull';
            }
        },
        touchmove(e) {
            this.currentY = e.touches[0].clientY;
            let distance = (this.currentY - this.startY) / 2;

            this.direction = distance > 0 ? 'down' : 'up';

            if (this.direction === 'down' && this.$el.scrollTop === 0 && this.topStatus) {
                this.distance = distance;

                if (distance > 0) {
                    e.preventDefault();
                }
                if (this.distance > this.topDistance) {
                    this.topStatus = 'drag';
                }
            }
        },
        touchend() {
            if (this.direction === 'down' && this.$el.scrollTop === 0) {
                this.transition = true;
                if (this.topStatus === 'drag') {
                    this.styleTop = {
                        marginTop: '10px'
                    };
                    if (!this.isLoading) {
                        this.isLoading = true;
                        this.$emit('loading');
                    }
                }
                this.distance = 0;
            }
            this.topStatus = '';
        }
    }
};
</script>

<style lang="scss">
.scroller-box {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}
.scroller-top {
    width: 43PX;
    height: 33PX;
    margin: 0 auto;
    margin-top: -43PX;
    margin-bottom: 10PX;
    background-image: url(~statics/img/pull_sprite.png);
    background-position: -1160PX 0;
    background-repeat: no-repeat;
    background-size: 1247PX 33PX;
    animation: loadingScroller 1.5s steps(29) .5s infinite;
}
@keyframes loadingScroller {
    0% { background-position: 0 0; }
    100% { background-position: -1247PX 0; }
}
</style>