vue下拉刷新组件的开发及slot的使用详解


Posted in Vue.js onDecember 23, 2020

“下拉刷新”和“上滑加载更多”功能在前端、尤其是移动端项目中非常重要,这里笔者由曾经做过的vue项目中的“blink”功能和各位探讨下【下拉刷新】组件的开发:

正式开篇

在前端项目的 components 文件夹下新建 pullRefreshView 文件夹,在其中新建组件 index.vue:(它代表“整个屏幕”,通过slot插入页面其他内容而不是传统的设置遮罩层触发下拉刷新)

首先需要编写下拉刷新组件的 template,这里用到 <slot>,代码如下:

<template>
	<div class="pullRefreshView" @touchmove="touchmove" @touchstart="touchstart" @touchend="touchend">
		<div ref="circleIcon" class="circle-icon">
			<div ref="circleIconInner" class="circle-icon-inner"></div>
		</div>
		<slot></slot>
	</div>
</template>

上面代码中,最外层使用了一个 div 用来包裹,作为事件绑定的容器,同时新建一个圆形 icon 的 div .circleIcon,我们将此 icon 样式设置在屏幕外,达到隐藏的效果,代码如下:

<style>
	.circle-icon{
		position: absolute;
		left: 0.625rem;
		top: -1.875rem;
	}
	.circle-icon-inner{
		width: 1.5625rem;
		height: 1.5625rem;
		background-image: url('圆圈图片地址');
		background-size: cover;
	}
	.circle-rotate{
		animation: xuzhuan .8s linear infinite;
	}
	@keyframes xuzhuan{
		0%{}
		25%{}
		50%{}
		75%{}
		100%{}
	}
</style>

下拉刷新组件的 UI 基本编写完毕,接下来就要绑定事件了,通过上述分析,加上我们之前章节开发图片查看器的原理,我们需要用到移动端 touchstart,touchmove,touchend 事件,可以实现下拉刷新效果。

首先,监听 touchstart 事件:

touchstart(evt){
	this.pullRefresh.dragStart=evt.targetTouches[0].clientY
	this.$refs.circleIcon.style.webkitTransition='none'
},

在 touchstart 事件中,我们主要做的是记录一些初始值,包括手指第一次接触屏幕时的位置,然后将圆形 icon 的动画效果先隐藏。

然后,监听 touchmove 事件:

touchmove(evt){
	if(this.pullRefresh.dragStart===null){
		return
	}
	let target=evt.targetTouches[0]
	// 向上滑为正,向下拉为负
	this.pullRefresh.percentage=(this.pullRefresh.dragStart-target.clientY)/window.screen.height
	let scrollTop=document.documentElement.scrollTop || document.body.scrollTop
	if(scrollTop===0){
		//this.pullRefresh指data中的pullRefresh对象(下方有),而evt即事件event参数
		if(this.pullRefresh.percentage<0 && evt.cancelable){
			evt.preventDefault()
			this.pullRefresh.joinRefreshFlag=true
			let translateY=-this.pullRefresh.percentage*this.pullRefresh.moveCount
			if(Math.abs(this.pullRefresh.percentage)<=this.pullRefresh.dragThreshold){
				let rotate=translateY/30*360
				this.$refs.circleIcon.style.webkitTransform='translate3d(0'+translateY+'px,0) rotate('+rotate+'deg)'
			}
		}else{
			if(this.pullRefresh.joinRefreshFlag===null){
				this.pullRefresh.joinRefreshFlag=false
			}
		}
	}else{
		if(this.pullRefresh.joinRefreshFlag===null){
			this.pullRefresh.joinRefreshFlag=false
		}
	}
},

在 touchmove 事件里,我们主要做的是根据手指移动的量来实时将圆形 icon 移动并旋转,这里有几点确实要说明一下:

  • 我们的下拉刷新触发的时机是在页面处于屏幕顶部并且手指向下拖动,这两个条件,缺一不可,在代码中,我们利用 scrollTop == 0this.pullRefresh.percentage < 0 来判断。
  • 在进入下拉刷新状态时,此时手指不断向下拖动,首先圆形 icon.circleIcon 会向下滚动并旋转,当滚动到临界值时就只原地旋转。
  • 如果手指在向上拖动,圆形 icon.circleIcon 就会向上滚动并旋转。
  • 直到手指离开屏幕前,都不会触发下拉刷新,只是圆形 icon.circleIcon 在不停的上下移动。

监听 touchend 事件:

touchend(evt){
	if(this.pullRefresh.percentage===0){
		return
	}
	if(Math.abs(this.pullRefresh.percentage)>this.pullRefresh.dragThreshold && this.pullRefresh.joinRefreshFlag){
		this.$emit('onRefresh')
		this.$refs.circleIconInner.classList.add('circle-rotate')
		setTimeout(()=>{
			this.$refs.circleIconInner.classList.remove('circle-rotate')
			this.$refs.circleIcon.style.webkitTransition='330ms'
			this.$refs.circleIcon.style.webkitTransform='translate3d(0,0,0) rotate(0deg)'
		},700)
	}else{
		if(this.pullRefresh.joinRefreshFlag){
			this.$refs.circleIcon.style.webkitTransition='330ms'
			this.$refs.circleIcon.style.webkitTransform='translate3d(0,0,0) rotate(0deg)'
		}
	}
	this.pullRefresh.joinRefreshFlag=null
	this.pullRefresh.dragStart=null
	this.pullRefresh.percentage=0
}

在 touchend 事件中,我们主要是做一些动画执行的操作,大家可以看看代码中的注释,这里说明一下:

  1. 此时手指离开屏幕,位移量达到临界值时,并且也有进入下拉刷新的标志位,就表明要触发正在刷新。此时圆形 icon原地旋转,并触发下拉刷新回调方法,延迟 700ms 后向上收起。
  2. 我们在实现圆形 icon 时的旋转和位移动画时,用了两个 div,在 touchmove 时,我们主要对外层的 div 也就是 ref=circleIcon,来实现位移和旋转。
  3. 在 touchend 时,我们主要给内层的 div 也就是 ref=circleIconInner 来加 animation 动画,因为无法给一个 div 同时使用位移旋转和 animation 动画,所以这里一个技巧就是给父元素设置位移和旋转,它的子元素在不设置任何 CSS 动画样式时,是会随着父元素而生效的。

最后,我们看下【data】中都有什么:

data(){
	return{
		pullRefresh:{
			dragStart:null, //开始抓取标志位
			percentage:0, //拖动量(百分比)
			dragThreshold:0.3, //临界值
			moveCount:200, //位移系数,可以调节圆形图片icon的运动速率
			joinRefreshFlag:null, //进入刷新状态的标志位(true)
		}
	}
},

补充:slot

<template>中为什么有<slot>

slot有三种形式:

  1. 普通插槽
  2. 具名插槽
  3. 作用域插槽

可能我们一般用具名slot的时候比较多,但是第一种也格外好用——正因为它没有名字,所以引用这个组件的另一个组件中包裹其中的所有内容都归这个slot所有:

假定my-component组件中有如下模板:

<div>
	<h2>我是子组件</h2>
	<slot>只有在没有内容分发的情况下这句话才会出现</slot>
</div>

父组件模板:

<div>
	<h1>这是父组件地盘</h1>
	<my-component>
		<p>这是一些初始内容</p>
		<p>这是更多的内容</p>
	</my-component>
</div>

最后就会被渲染成这样:

<div> 
	<h1>这是父组件地盘</h1>
	<div> 
		<h2>我是子组件</h2>
		<p>这是一些初始内容</p>
		<p>这是更多的内容</p>
	</div> 
</div>

所以这里这样做,就是为了在“父组件”中调用时让“下拉的动画”更自然,但又不会增加一个文件的负担。

到此这篇关于vue下拉刷新组件的开发及slot的使用详解的文章就介绍到这了,更多相关vue下拉刷新组件slot使用内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Vue.js 相关文章推荐
Vue项目利用axios请求接口下载excel
Nov 17 Vue.js
ESLint 是如何检查 .vue 文件的
Nov 30 Vue.js
详解Vue 的异常处理机制
Nov 30 Vue.js
vue祖孙组件之间的数据传递案例
Dec 07 Vue.js
vue实现按钮切换图片
Jan 20 Vue.js
vue 使用饿了么UI仿写teambition的筛选功能
Mar 01 Vue.js
vue实现桌面向网页拖动文件的示例代码(可显示图片/音频/视频)
Mar 01 Vue.js
vue+django实现下载文件的示例
Mar 24 Vue.js
vue项目支付功能代码详解
Feb 18 Vue.js
vue如何使用模拟的json数据查看效果
Mar 31 Vue.js
vue ref如何获取子组件属性值
Mar 31 Vue.js
Vue OpenLayer 为地图绘制风场效果
Apr 24 Vue.js
Vue3 实现双盒子定位Overlay的示例
Dec 22 #Vue.js
详解Vue的异步更新实现原理
Dec 22 #Vue.js
Vue组件简易模拟实现购物车
Dec 21 #Vue.js
vue实现购物车的小练习
Dec 21 #Vue.js
Vue实现小购物车功能
Dec 21 #Vue.js
vue监听滚动事件的方法
Dec 21 #Vue.js
vue el-upload上传文件的示例代码
Dec 21 #Vue.js
You might like
PHP使用SWOOLE扩展实现定时同步 MySQL 数据
2017/04/09 PHP
PHP addcslashes()函数讲解
2019/02/03 PHP
Yii框架连表查询操作示例
2019/09/06 PHP
javascript 年月日联动实现核心代码
2009/12/21 Javascript
JavaScript自定义DateDiff函数(兼容所有浏览器)
2012/03/01 Javascript
Javascript 中 null、NaN和undefined的区别总结
2013/04/10 Javascript
JavaScript中常用的六种互动方法示例
2015/03/13 Javascript
bootstrap 表单验证使用方法
2017/01/11 Javascript
ES6新特性之函数的扩展实例详解
2017/04/01 Javascript
Bootstrap.css与layDate日期选择样式起冲突的解决办法
2017/04/07 Javascript
jquery 禁止鼠标右键并监听右键事件
2017/04/27 jQuery
vue如何获取点击事件源的方法
2017/08/10 Javascript
Vue导出json数据到Excel电子表格的示例
2017/12/04 Javascript
vue内置指令详解
2018/04/03 Javascript
React 源码中的依赖注入方法
2018/11/07 Javascript
js时间戳转yyyy-MM-dd HH-mm-ss工具类详解
2019/04/30 Javascript
JS寄快递地址智能解析的实现代码
2020/07/16 Javascript
[03:42]2014DOTA2国际邀请赛 第三日比赛排位扑朔迷离
2014/07/12 DOTA
[01:09:16]DOTA2-DPC中国联赛 正赛 SAG vs Dynasty BO3 第一场 1月25日
2021/03/11 DOTA
将Python中的数据存储到系统本地的简单方法
2015/04/11 Python
使用Python编写提取日志中的中文的脚本的方法
2015/04/30 Python
如何使用Python的Requests包实现模拟登陆
2018/04/27 Python
Python实现的合并两个有序数组算法示例
2019/03/04 Python
python命令行参数用法实例分析
2019/06/25 Python
python实现连连看辅助之图像识别延伸
2019/07/17 Python
python Qt5实现窗体跟踪鼠标移动
2019/12/13 Python
Python2手动安装更新pip过程实例解析
2020/07/16 Python
学校门卫工作职责
2013/12/07 职场文书
社团文化节策划书
2014/02/01 职场文书
安全生产活动月方案
2014/03/09 职场文书
市委召开党的群众路线教育实践活动总结大会报告
2014/10/21 职场文书
婚礼答谢词
2015/01/04 职场文书
导游词之铁岭象牙山
2019/12/06 职场文书
嵌入式Redis服务器在Spring Boot测试中的使用教程
2021/07/21 Redis
【海涛dota解说】DCG联赛第一周 LGD VS DH
2022/04/01 DOTA