最近接触vue.js移动端开发。自己写一个类似微博的图片预览器来学习一下移动端手势的实现和css3的属性的使用。
目标分析
首先分析图片预览器的功能:
1.图片显示
2.缩放(swipe)图片3.拖拽图片(drag)3.双击(doubleTap)放大/缩小4.单击(tap)隐藏图片5.左右滑到前一页或后一页分析的手势有:单击(tap) 双击(doubleTap) 缩放(swipe) 拖拽(drag)
都是由有三个事件构成:touchstart, touchmove,touchend.手势解析:
单击(tap)
手势分解:点击进入touchstart事件,touchmove几乎没有,但是也要为用户预留点击时的微小移动,再触发touchend事件。在本组件中touchmove移动半径设置为小于10
从触摸屏幕到离开屏幕的时间是非常短的,所以touchstart事件到touchend事件之间的时间间隔是非常小的,不超过500毫秒。t2-t1<500
onTouchstart(evt){ this.startTime = new Date().getTime(); if(evt.touches.length>1){//双手势 }else{//单手势 this.start.x = evt.touches[0].pageX; this.start.y = evt.touches[0].pageY; } } onTouchMove(evt){ this.move.x = evt.touches[0].pageX; this.move.y = evt.touches[0].pageY; } onTouchEnd(evt){ let timestamp = new Date().getTime(); if(this.move.x !== null && Math.abs(this.move.x - this.start.x)< 10 ||this.move.y !== null && Math.abs(this.move.y - this.start.y)<10){ //有移动的情况 }else{ //单击 if(timestamp - this.startTime < 500){ //触发单击事件 } } }
双击(doubleTap)
双击事件包含了两次单击事件,区分在于两次单击的时间判断,两次单击的时间,也就是touchstart触发的时间间隔,不超过300毫秒,当然,也要给用户一些触摸移动的像素,将两个点的x,y轴上的距离控制在10像素内。实现:
touchStartFn(evt){ this.startTime = new Date().getTime(); if(evt.touches.length>1){//双手势 }else{//单手势 this.start.x = evt.touches[0].pageX; this.start.y = evt.touches[0].pageY; if(this.previousTouchPoint){//上一次的触摸点 if(Math.abs(this.start.x- this.previousTouchPoint.startX)<10 &&Math.abs(this.start.y- this.previousTouchPoint.startY)<10 && this.startTime - this.previousStartTime < 300){ //触发双击的事件 ... } } this.previousTouchTime = this.startTime; this.previousTouchPoint = { startX:this.start.x, startY:this.start.y }; } }
如果一个页面上既有单击tap事件,又有双击doubleTap事件,怎么办?用上述方法,两种事件会相互冲突,光靠时间和偏移量来控制是不够的。
解决思路:初始化this.previousTouchTime = 0;this.previousTouchPoint = undefined;在执行完双击事件后将this.previousTouchPoint置为undefined,this.previousTouchTime置为0。在触摸结束事件中添加一个setTimeout来监听是否有新的单击事件发生。如果新单击事件发生,则去除这个监听,如果没有,则触发单击事件touchEndFn(evt){ let timer = setTimeout(()=>{ if(this.previousTouchPoint !== undefined && this.previousTouchTime !== 0){ //触发单击事件 ... }else{ clearTimeout(timer);//去除监听 } },300); }
这样,单击和双击事件就不会冲突了。
缩放(swipe)
缩放是个双手势触摸过程,触摸点会有两个,也就是Touch对象会有两个。并在触摸滑动过程中,要计算出缩放倍数。
onTouchStart(evt){ if(evt.touches.length>1){//双手势 let point1 = evt.touches[0]; let point2 = evt.touches[1]; let deltaX = Math.abs(point2.pageX - point1.pageX); let deltaY = Math.abs(point2.pageY - point1.pageY); this.distance = Math.sqrt(deltaX*deltaX+deltaY*deltaY);//初始时候的距离,r1 }else{ //单手势事件 }}onTouchMove(evt){ if(evt.touches.legnth>1){ let point1 = evt.touches[0]; let point2 = evt.touches[1]; let deltaX = Math.abs(point2.pageX - point1.pageX); let deltaY = Math.abs(point2.pageY - point1.pageY); let distance = Math.sqrt(deltaX*deltaX+deltaY*deltaY); if(this.distance){ this.swipeScale = distance/this.distance; //执行缩放事件 } }else{//单手势事件}}
拖拽/移动
移动事件就是在触摸屏幕并移动的时候,图片或者元素能够跟随手指一起移动。这是一个单手势操作,屏幕上只要一个触摸点。在touchmove发生时将坐标位置向减得到位移量。
onTouchStart(evt){ if(evt.touches.length>1){//双手势 }else{//单手势 this.start.x = evt.touches[0].pageX; this.start.y = evt.touches[0].pageY; } } onTouchMove(evt){ if(evt.touches.length>1){//双手势 }else{//单手势 let deltaX = evt.touches[0].pageX - this.start.x; let deltaY = evt.touches[0].pageY- this.start.y; //触发移动事件 } }
图片放大功能的实现
我采用了css3的transform属性进行缩放,并且设置transform-origin来设置缩放中心位置。缩放的倍数为缩放事件计算出的this.swipeScale
缩放代码:$img.style.transform = "scale("+this.swipeScale +")"; $img.style.transformOrigin = x + " " + y;
下一节将讲用vue.js具体实现过程