手机端实现触摸拖拽效果
前言
相信在开发手机端页面时,大家都会碰到拖拽元素的需求,下面我们就来介绍一下相关的实现过程,我们需要使用到touchstart,touchmove,touchend三个事件,
1.touchstart是触摸开始时触发的事件
2.touchmove是触摸移动过程中触发的事件
3.touchend是触摸结束时触发的事件
我们遇到这样一个需求,初始状态,底部会展示一小部分区域,当我们手指触摸到上面时,向上滑动,底部区域会随着手指移动而跟着向上移动,分为以下优化情况
1.当在底部时,快速向上滚动然后放开,底部应该正常的向上滚动到顶部,
2.当在底部时,缓慢向上滚动然后停止一段时间然后放开,应该根据停止的位置进行是回到底部还是到顶部
3.当在顶部时,快速向下滚动然后放开,应该正常的向下滚动到底部,
4.当在顶部时,缓慢向下滚动然后停止一段时间然后放开,应该根据停止的位置进行是回到底部还是到顶部
这里有三块,1.首页,2.底部区域,3.底部内
难点,底部区域初始态,底部区域滚动过程态,底部区域滚动终止态
底部区域初始态:首页要可滚动,底部区域要可触发,底部内要不可滚动
底部区域滚动过程态:当底部区域进行触摸跟随移动时,需要阻止首页的滚动,也需要阻止底部内的滚动,因为事件冒泡会触发滚动和触摸事件
底部区域滚动终止态:当底部区域移动到最上方时,底部内就需要可以进行滚动,
2.1当手指向上滚动时,需要阻止底部区域的触摸事件不触发,只触发底部内的滚动事件
2.2当手指向下滚动时,指直到无法在进行滚动,已经滚动到最顶部了,需要触发底部区域的触摸事件,阻止底部内的滚动事件
下图是移动到最上方的状态
下面是我们具体实现代码
//html代码
<div
id="dialogWrap"
:class="['dialogWrap', showBoard ? 'active' : 'normal']"
@touchstart="dialogWrapStart"
@touchmove="dialogWrapMove"
@touchend="dialogWrapEnd"
>
<div class="dialogHeader">
<div class="headerLine"></div>
</div>
<div id="dialogContent"
class="dialogContent"
@scroll="dialogContentScroll">
</div>
</div>
//js代码
//定义datas
flags = false;
position = { x: 0, y: 0 };
nx = 0;
ny = 0;
dx = 0;
dy = 0;
xPum = 0;
yPum = 0;
beforePum = 0; //移动前一刻位置
pausePum = 0;//移动暂停时位置
isTouchStart = false;//是否开始触摸事件
bottomHeight = 0; // 底部高度
//底部内
private get dialogContent(): HTMLElement {
return document.getElementById('dialogContent');
}
//底部区域
private get dialogWrap(): HTMLElement {
return document.getElementById('dialogWrap');
}
//首页
private get bodyEle(): HTMLElement {
return document.body;
}
mounted(): void {
this.bottomHeight = window.innerHeight / (window.innerWidth / 3.6) - 1.19;
}
//内部滚动
private dialogContentScroll(e): void {
e.stopPropagation();
let scrollTops = Math.ceil(this.dialogContent.scrollTop); //滚动的距离
let clientHeight = this.dialogContent.clientHeight; //窗口的高度
let scrollHeight = this.dialogContent.scrollHeight; //内容的总高度
// console.log('--------------------', 0, scrollTops, clientHeight, scrollHeight);
if (scrollTops === 0) {
// 滚动到最上方时,变为false,可以触发底部区域的触摸事件
this.isTouchStart = false;
} else {
// 除了滚动到最上方时,变为true,不可以触发底部区域的触摸事件
this.isTouchStart = true;
}
}
//触摸开始
private dialogWrapStart(event): void {
event.stopPropagation();
if (this.isTouchStart) {
return;
}
this.bodyEle.style.overflow = 'hidden'; //阻止首页滚动
this.dialogContent.style.overflow = 'hidden'; //阻止底部内滚动
this.dialogWrap.style.transition = null;
this.flags = true;
let touch;
if (event.touches) {
touch = event.touches[0];
} else {
touch = event;
}
this.position.x = touch.clientX;
this.position.y = touch.clientY;
this.dx = this.dialogWrap.offsetLeft;
this.dy = this.dialogWrap.offsetTop;
}
//触摸移动
private dialogWrapMove(event): void {
event.stopPropagation();
if (this.flags) {
let touch;
if (event.touches) {
touch = event.touches[0];
} else {
touch = event;
}
this.nx = touch.clientX - this.position.x;
this.ny = touch.clientY - this.position.y;
this.xPum = this.dx + this.nx;
this.yPum = this.dy + this.ny;
setTimeout(() => {
this.beforePum = this.yPum - 1;
this.pausePum = this.yPum;
}, 10);
if (this.yPum >= 50 && this.yPum <= 650) {
this.dialogWrap.style.top = this.yPum + 'px';
}
// console.log(this.yPum);
if (this.yPum <= 600) {
//滚动到一定距离,加载后面的模块,性能优化初始状态只加载底部区域的部分内容
this.isScroll = true;
}
//阻止页面的滑动默认事件;如果碰到滑动问题,1.2 请注意是否获取到 touchmove
document.addEventListener(
'touchmove',
function () {
event.preventDefault();
},
false
);
}
}
// 向上的滚动
private handelTop(): void {
this.dialogWrap.style.top = 0.5 + 'rem';
this.dialogContent.style.overflow = 'scroll';//开启底部内滚动
this.$emit('showDialog');
this.isScroll = true;
}
// 向下的滚动
private handelBottom(): void {
this.dialogWrap.style.top = this.bottomHeight + 'rem';
this.dialogContent.style.overflow = 'hidden';//阻止底部内滚动
this.bodyEle style.overflow = 'scroll'; //开启首页滚动
this.$emit('hideDialog');
}
//触摸结束
private dialogWrapEnd(): void {
this.flags = false;
if (this.isTouchStart) {
return;
}
this.dialogWrap.style.transition = 'top 0.3s';
console.log('this.yPum', this.yPum, 'this.beforePum', this.beforePum, this.pausePum, Math.abs(this.xPum));
if (this.yPum < this.beforePum) {
//当this.yPum小于this.beforePum时,当手指迅速向上滑动
this.handelTop();
}else if(this.yPum == this.pausePum) {
//当this.yPum等于this.beforePum时,也就是滚动到中间停止一段时间的情况
//根据this.yPum是否滚动距离超过一半来,区分是向上还是向下
if(this.yPum >= 330 && this.dialogWrap.style.top !== '0.5rem') {
this.handelBottom();
}else if (this.yPum <= 330 && this.yPum > 0) {
this.handelTop();
}
} else if(this.yPum > this.beforePum) {
//当this.yPum大于this.beforePum时,当手指迅速向下滑动
if(Math.abs(this.xPum) >= 30) {
//当水平位移大于30,向上,为了防止banner水平位移导致的向下收起面板
this.handelTop();
}else {
this.handelBottom();
}
}
console.log(this.yPum, 2);
this.flags = false;
}