移动端touch事件:
常见的触摸事件:
touchstart:(手指触摸屏幕时触发),
touchmove:(手指移动时触发),
touchend:(手指离开屏幕时触发)
跟踪触摸的属性:
touches:表示当前跟踪的触摸操作的touch对象的数组。
targetTouches:特定于事件目标的Touch对象的数组。
(正在触摸当前 DOM 元素上的手指的一个列表。)
changeTouches:表示自上次触摸以来发生了什么改变的Touch对象的数组。
每个touch对象包含的属性:
Touch.identifier:此 Touch 对象的唯一标识符。 一次触摸动作(我们指的是手指的触摸)在平 面上移动的 整个过程中,该标识符不变。 可以根据它来判断跟踪的是否是同一次触摸过程,此值为只读属性。
Touch.screenX:触点相对于屏幕左边沿的X坐标。只读属性。
Touch.screenY:触点相对于屏幕上边沿的Y坐标。只读属性
Touch.clientX:触点相对于可见视区(visual viewport)左边沿的X坐标。不包括任何滚动偏移。只读属性。
Touch.clientY:触点相对于可见视区(visual viewport)上边沿的Y坐标。不包括任何滚动偏移。只读属性。
Touch.pageX:触点相对于HTML文档左边沿的X坐标。当存在水平滚动的偏移时,这个值包含了水平滚动的偏移。只读属性。
Touch.pageY:触点相对于HTML文档上边沿的Y坐标。当存在水平滚动的偏移时,这个值包含了垂直滚动的偏移。只读属性。
Touch.radiusX:能够包围用户和触摸平面的接触面的最小椭圆的水平轴(X轴)半径。这个值的单位和 screenX 相同。只读属性。
Touch.radiusY:能够包围用户和触摸平面的接触面的最小椭圆的垂直轴(Y轴)半径。这个值的单位和 screenY 相同。只读属性。
Touch.rotationAngle:它是这样一个角度值:由radiusX 和 radiusY描述的正方向的椭圆,需要通过顺时针旋转这个角度值,才能最精确地覆盖住用户和触摸平面的接触面。只读属性。
Touch.force:手指挤压触摸平面的压力大小,从0.0(没有压力)到1.0(最大压力)的浮点数。只读属性。
Touch.target:当这个触点最开始被跟踪时(在 touchstart 事件中),触点位于的HTML元素。
需求:模拟贝壳找房弹出框效果。
- 当手指在框内区域按下并移动时,弹出框跟随手指移动距离改变其高度,手指离开时弹到最高点,高度变化过程中,列表元素不发生滚动。
- 弹框在最高点时,手指向下滑动时,列表下移,列表到达scrollTop为0时,改变弹框高度。
- 手指上移弹到最高点,手指下移弹到最低点
//touch事件无效时加上.native修饰符即可解决
//(该修饰符的作用就是把一个vue组件转化为一个普通的HTML标签)
@touchstart.native="touchstart"
@touchmove.native="touchmove"
@touchend.native="touchend"
动态获取屏幕高度,并将弹出框最高点、最低点、判断点分别设置为屏幕高度的3/4、1/3、2/3
this.bodyHeight = document.documentElement.clientHeight
this.halfHeight = (this.bodyHeight / 3) * 2
this.lowHeight = this.bodyHeight / 3
this.highHeight = (this.bodyHeight / 4) * 3
handler(ev){
// console.log('事件名称',ev)
ev.preventDefault();
},
//滑动事件start-------------------------------------------------------
touchstart(event) {
// 判断是否达到最大高度,若未达到最大高度,添加阻止默认事件,让list不滚动
if(this.$refs.tabsHeaderRef.$el.style.height!= this.highHeight + 'px'){
//在事件发生期间,调用 preventDefault()事件可以阻止滚动。
window.addEventListener("touchmove", this.handler, { passive: false });
}
var touch = event.touches[0];
this.startY = touch.pageY;//触发点位置
this.startX = touch.pageX;
// 获取手指按下时的元素高度,若为0,则是第一次渲染元素,赋值最低点px
if(this.startHeight==0){
this.startHeight = Number(this.lowHeight)
this.$refs.tabsHeaderRef.$el.style.height=this.lowHeight + 'px'
}
else{
let hh=this.$refs.tabsHeaderRef.$el.style.height
this.startHeight = Number(hh.substring(0,hh.indexOf('p')));
}
},
touchmove(event) {
// alert(event)
var touch = event.touches[0];
//当前移动距离
this.endY = this.startY - touch.pageY;
this.endX = this.startX - touch.pageX;
this.touchY = touch.pageY
//判断弹出框是否在最高点
if(this.$refs.tabsHeaderRef.$el.style.height == this.highHeight + 'px'){
const body = document.getElementById("positionValue"); // 获取滚动条的dom
// 获取滚动框距离顶部的距离
const scrollTop = body.scrollTop
// 获取可视区的高度
const windowHeight = body.clientHeight;
// 获取滚动条的总高度
const scrollHeight = body.scrollHeight;
//当弹出框在最高点、移动距离小于0(手指向下移动)且滚动狂距顶部位置为0(即滚动条滚动到最上方)
if(this.endY < 0 && scrollTop == 0){
//将弹出框高度设置为手指按下时高度 + 移动距离
this.$refs.tabsHeaderRef.$el.style.height = (this.startHeight + this.endY)+'px';
this.$refs.tabsHeaderRef.$el.style.transition = 'all -.29s ease-in-out';
}
// 下拉刷新不起作用,此处采用判断当前是否滑动到底部,滑动到底部是触发加载事件,偶尔会有1px之类的误差,所以+100
// 因为移动事件一直被触发,所以对加载事件做了防抖
if(scrollTop + windowHeight + 100 >= scrollHeight){
this.debounce(this.onLoad(), 500)
}
}
if(this.$refs.tabsHeaderRef.$el.style.height != this.highHeight + 'px'){
// this.listIsShow = true;
this.$refs.tabsHeaderRef.$el.style.height = (this.startHeight + this.endY)+'px';
this.$refs.tabsHeaderRef.$el.style.transition = 'all -.29s ease-in-out';
}
},
//防抖函数
debounce(fn, delay) {
// 定时器
let timer = null
// 将debounce处理结果当作函数返回
return function () {
// 保留调用时的this上下文
let context = this
// 保留调用时传入的参数
let args = arguments
// 每次事件被触发时,都去清除之前的旧定时器
if(timer) {
clearTimeout(timer)
}
// 设立新定时器
timer = setTimeout(function () {
fn.apply(context, args)
}, delay)
}
},
touchend(event) {
//手指离开元素时,清除 preventDefault()事件。
window.removeEventListener("touchmove", this.handler, { passive: false });
if (this.endY>0) {
this.$refs.tabsHeaderRef.$el.style.height = this.highHeight + 'px'
this.$refs.tabsHeaderRef.$el.style.transition = 'all .2s ease-in-out'
} else {
if(this.$refs.tabsHeaderRef.$el.style.height != this.highHeight + 'px'){
if(this.$refs.tabsHeaderRef.$el.style.height < this.lowHeight + 'px'){
this.$refs.tabsHeaderRef.$el.style.height = '0px'
this.$refs.tabsHeaderRef.$el.style.transition = 'all .2s ease-in-out'
this.customerInfo = false
}
else{
this.$refs.tabsHeaderRef.$el.style.height = this.lowHeight + 'px'
this.$refs.tabsHeaderRef.$el.style.transition = 'all .2s ease-in-out'
}
}
}
this.endY = 0;
},
//滑动事件end----------------------------------------------------------------