vue 实现上拉加载下拉刷新(思路贼清晰)

8,576 阅读3分钟

项目需要用到上拉刷新下拉加载 所以自己手动实现了一个 组件:

<template>

	<div class="my-scroll" :class="[scrollState?'prohibit':'allow']" ref="myScroll" @scroll.passive="onScroll($event)" @touchstart="touchStart($event)" @touchmove="touchMove($event)" @touchend="touchEnd($event)"  >
		<div class="scroll-top" :style="{height:top+'px'}">
			<div v-if="aspect==2">
				<p v-if="state==6">
					下拉刷新
				</p>
				<p v-if="state==1">
					<i><img :src="Load"/></i>
					<br/>
					刷新中
				</p>
				<p v-if="state==2">松开刷新</p>
				<p v-if="state==3">
					<i><img :src="Load"/></i>
					<br/>
					刷新完成
				</p>
			</div>
		</div>
		<!-- top -->
		<div class="scroll-list" :style="{ transform: 'translate3d(0, ' + top + 'px, 0)'}">
			<slot name='scrollList'></slot>
			<div class="scroll-bottom">
				<div v-if="state==4">加载中</div>
				<div v-if="state==5">加载完成</div>
				<div v-if="state==7">没有更多</div>
			</div>
		</div>
	</div>
</template>
<script type="text/javascript">
import tween from '@/plugins/tween'
import Load from '@/assets/Load.gif'
	export default {
		name:'myScroll',
		props:{
			'page':{
				type:Object,  //counter:当前页  pageStart:开始页数  pageEnd:结束页数  total:总页数
			},
			'onRefresh':{ //刷新回调
				type:Function,
				require:true
			},
			'onPull':{ //加载回调
				type:Function,
				require:true
			},
			'getScrollTop':{ //获取滚动条位置
				type:Function
			},
			'setScrollPage':{ //改变滚动条位置
				type:Function
			},
			'scrollState':{//是否可滑动
				type:Boolean,
				require:true
			}
		},
		data(){
			return {
				Load,
				pageX:0,
				pageY:0,
				state:0, 
				scrollPosition:0,
				myScroll:null,
				myScrollList:null,
				top:0,
				aspect:0, //1:向下 2:向上
				listHeight:0,
 			}
		},
		created(){
		},
		methods:{
			ScrollTop(top){ //修改滚动条位置
				this.myScroll.scrollTop = top
			},
			/*
			 * 刷新中:1
			 * 松开刷新:2
			 * 刷新完成:3
			 * 加载中:4
			 * 加载完成:5
			 * 下拉刷新:6
			 * 没有更多:7
			 */
			setState(index){ //修改状态
				this.state = index
		        if(index == 5||index == 3){
					setTimeout(()=>{
						this.state = 0
						let timer = null;
						let that = this;
						var b=50,c=100,d=100,t=0;
						cancelAnimationFrame(timer);
						timer = requestAnimationFrame(function fn() {
							var oTop = that.top;
							if (that.top>0) {
								that.top = Math.ceil(tween.Quart.easeInOut(10,oTop,8,10)) - 15
								timer = requestAnimationFrame(fn);
							} else {
								cancelAnimationFrame(timer);
								that.top = 0
							}
						});
					},500)
				}
			},
			touchStart(e){ //触摸事件
				this.pageX = e.targetTouches[0].pageX
				this.pageY = e.targetTouches[0].pageY
			},
			touchMove(e){ //触摸滑动事件
				this.scrollPosition = this.myScroll.scrollTop //获取滚动条位置
				if(this.scrollState&&e.targetTouches[0].pageY>this.pageY){ //向上滑动
					this.aspect = 2
					if(this.myScroll.scrollTop==0){
						let diff = e.targetTouches[0].pageY - this.pageY - this.scrollPosition
						this.top = Math.pow(diff, 0.9)
						let ranget = diff/document.body.clientHeight*100 //计算在屏幕上滑动了多少
						if(ranget > 20){
							this.state = 2
						}else if(ranget < 15){
							this.state = 6
						}
						 e.preventDefault()
					}
				}else if(this.scrollState&&this.state!=4){ //向上滑动
					this.aspect = 1
				}
				
			},
			touchEnd(e){
				if(this.aspect == 2&&this.state == 2||this.state == 1){ //上拉处理
					this.top = 100
					this.state = 1
					this.topCallback()
				}else if(this.aspect == 2){
					this.state = 0
					this.top = 0
				}
			},
			onScroll(e){
				let listHeight = this.myScrollList.offsetHeight //列表总高度
				let listScrollTop = e.target.scrollTop + this.myScroll.offsetHeight //当前滚动条位置

				if(this.state == 0&&listHeight-listScrollTop < 100){
					this.bottomCallback()
				}

				if(this.getScrollTop)this.getScrollTop(e.target.scrollTop) //返回X,Y
			},
			topCallback(){ //刷新回调
				this.onRefresh(this.state)
			},
			bottomCallback(){ //加载回调
				if(this.state != 7){
					this.state = 4
					this.onPull(this.state)
				}
				
			},
		},
		mounted(){
			this.myScroll = this.$refs.myScroll //获取滑条dom
			this.myScrollList = this.myScroll.children[1] //获取列表dom
		}
	}
</script>
<style lang="scss" scoped>
	.allow{
		overflow:hidden;
		height: auto;
	}
	.prohibit{
		max-width: 100%;
		max-height: 100%;
	    height: 100%;
		overflow:hidden;
		overflow-y: scroll;
		-webkit-overflow-scrolling: touch;
		will-change: transform;
	    transition: all 450ms;
	    backface-visibility: hidden;
	    perspective: 1000;
	}
	.my-scroll{
	    position: relative;
		color: #fff;
	    .scroll-top{
	    	text-align: center;
	    	display:flex;
	    	position:absolute;
	    	top:0;
	    	left:0;
			width:100%;
			overflow: hidden;
	    	div{
	    		display:flex;
				height:auto;
	    		width:100%;
	    		justify-content: center;
	    		align-items:center;
	    		flex-wrap: wrap;
	    		i{
	    			flex:1 0 100%;
					display:block;
					height: 0.4rem;
	    		}
	    		img{
	    			width:0.6rem;
	    		}
				p{
					flex:1 0 100%;
				}
	    	}
	    }
		.scroll-list{
			overflow:hidden;
			min-height: 100%;
		}
		.scroll-bottom{
			text-align: center;
			line-height: 40px;
		}
	}
</style>

使用:

<template>
	<div class="index">
        <my-scroll  ref="myScroll" :page="page" :on-refresh="onRefresh" :on-pull="onPull"  :get-scroll-top="getTop">
        <div slot="scrollList">
            <ul>
                <li v-for="(x,index) in list" :key="index">列表</li>
            </ul>
        </div>
        </my-scroll>
	</div>
</template>
<script type="text/javascript">
	import myScroll from '@/components/myScroll.vue'
	export default {
		data(){
			return{
				list:[],
				page:{
					counter:1,  
					pageStart:1,  
					pageEnd:1,  
					total:10
				},
			}
		},
		methods:{
			onRefresh(mun){ //刷新回调
					setTimeout(()=>{
						this.$refs.myScroll.setState(3);
					},500)
			},
			onPull(mun){ //加载回调
				if(this.page.counter<=this.page.total){
					setTimeout(()=>{
						this.page.counter++
						this.$refs.myScroll.setState(5)
						for(let i=0;i<10;i++){
							this.listdata.push({})
						}
					},500)
				}else{
					this.$refs.myScroll.setState(7)
				}
			},
			getTop(y) {//滚动条位置
	            
	        },

		},
		components:{
			myScroll
		},
		created(){

		},
		mounted(){
			for(let i=0;i<1*50;i++){
				this.list.push({})
			}
			
		},

	}
</script>
<style lang="scss" scoped>
	.index{
        
	}
</style>

上拉刷新实现思路: 1.通过touchstart获取用户第一次点击的坐标 2.通过touchmove 判断向上滑动还是向下 4.判断列表的滚动条是否在最顶部 5.然后判断在这个屏幕滑动的比例 进行状态显示 下拉加载实现思路: 1.通过判断滚动条位置实现下拉加载

样式:

这里写图片描述

注:缓动算法与tween.js 来自 www.cnblogs.com/cloudgamer/… 详细用法这里都有

github地址:github.com/FZliweilian…