这几个月事情比较多,写了一些博客都没有来得及整理发布,今天恰好有一位同事在开发前端页面的时候用到了手势判断。因此翻出了以前写的 demo,顺便整理一下做为记录。css
手势判断在各类应用中都十分常见,如 APP 中的手势翻页,前进后退等等,如微博作得就特别好,微信的话就不想吐槽了。不扯太远,H5 开发中手势判断通常多用于一些交互比较灵活的场景,例如大转盘抽奖游戏,旋转菜单,酷跑,打砖块游戏等等。今天不具体到这些小游戏的开发,咱们重点讲讲实现的原理。其实比较基础,大神请自动忽略。html
前提事件,所谓手势,就是你的手对于屏幕触摸的方向或者说轨迹。其实移动端只不过是用户的手指代替了 PC 端的鼠标。因此若是你作过 PC 端页面的鼠标轨迹判断,那移动端的手势判断思路也就能够秒杀了。前端
上面咱们提到,要对用户手指对屏幕的操做进行捕捉,在 H5 中提供了这样几个关键的事件监听 touchstart
、touchmove
、touchend
。由于比较基础我下面就简单带过了。web
首先 touchstart
顾名思义 “触摸-开始”,也就是当手指触摸到屏幕的时候将触发这个事件。其次 touchmove
“触摸-移动”。也就是当你的手指在屏幕上划动的时候该事件将不断被触发。最后 touchend
“触摸-结束” 当手指离开屏幕的那一瞬间触发。canvas
手指事件已经解决了,接下来就是想办法判断用户的手指是怎样划动的了。这里就须要引入一个十分基础的概念坐标系。咱们数学上的平面直角坐标系是这样ruby
而咱们屏幕上的坐标系是这样的:服务器
注意 Y 轴的增加方向和平面直角坐标系是相反的。微信
到这里基本上思路就已经肯定了,当手指触摸到屏幕的一瞬间也就是 touchstart
时记录手指触摸的位置(xstart,ystart),当手指离开屏幕时也就是 touchend
时获取手指离开屏幕的位置(xend,yend),而后进行判断,若是 xend - xstart > 0 那么水平方向是向右,反之向左。若是 yend - ystart > 0 那么垂直方向是向下,反之向上。固然机器是十分精细的,就算移动一个像素上述判断原则也将成立,人类是没有机器那么敏感的,因此这里顺便提一个东西,也就是灵敏度的问题,通常来讲咱们是不但愿移动一个像素也算是触发了某个手势的,下面讲到代码时会有所体现。app
手势状况图解:总共有八个方向框架
因为此次的项目已经使用了 zepto.js 这个框架,因此顺手在此基础上给它添加了本身的手势拓展方法。这里我就再也不累述给 zepto.js 添加拓展方法的知识点了。还不了解的朋友请先 Google(说到 Google 下篇文章记录一下如何搭建本身的服务器利用 ss ***吧)。咱们先上完整代码,在进行核心代码分析
完整代码以下:
$.fn.gesture = function(callback,sensibility){ if (!sensibility || isNaN(sensibility) || sensibility <= 0) sensibility = 130; var start_piont,end_point,delta_x,delta_y,distances; var direction = { horizontal : null, vertical : null } // var cond = 130;//灵敏度控制 this.bind('touchstart',function(e){ var touch = e.touches[0]; start_piont = { x: touch.pageX, y: touch.pageY } }).bind('touchmove',function(e){ /* var touch = e.touches[0]; end_point = { x: touch.pageX, y: touch.pageY }*/ }).bind('touchend', function(e){ var touch = e.changedTouches[0]; end_point = { x: touch.pageX, y: touch.pageY } delta_x = end_point.x - start_piont.x; delta_y = end_point.y - start_piont.y; if(delta_x < -sensibility) { // 向左划动 direction.horizontal='left'; } else if (delta_x > sensibility) { // 向右划动 direction.horizontal='right'; } if(delta_y > sensibility){ direction.vertical = 'down'; }else if(delta_y < (-sensibility)){ direction.vertical = 'up'; } if(typeof callback === 'function'){ callback(direction); } //还原状态 direction = { horizontal : null, vertical : null } });
注意:基于 zepto.js 的拓展方法,因此注意一下依赖。记得先引入 zepto.js
分析:
首先看下下面这句代码
$.fn.gesture = function(callback,sensibility = 130){...}
sensibility = 130
这个是 js 的默认参数值,这个在我以前的文章《开源原生JavaScript插件-CJPCD(省市区联动)》文章中有提到过,后面有点纰漏是 IOS 设备并不兼容,因此这里仍是采用了保险的作法:
if (!sensibility || isNaN(sensibility) || sensibility <= 0){ sensibility = 130; }
sensibility
就是上面提到的灵敏度的问题,笔者默认设置为 130 没为何。除此以外代码中还出现了另外几个关键的变量。
null
、left
或 right
null
、up
或者 down
代码值得注意的是:touchstart
和 touchmove
均可以经过 e.touches[0]
来获取当前的手指坐标。可是 touchend
中的 touches
(本质是 TouchList) 的长度为零 也就是说你没法经过这个来获取到手指离开时那个点的坐标 以下图
既然这样咱们来 touchmove 事件中作处理,在咱们手指在屏幕上划动的时候会不断调用该方法,那么咱们能够在该方法中不断得给 end_point
刷新它的坐标值(x,y)那么当咱们的手指离开屏幕,也就是再也不触发 touchmove
事件,但触发 touchend
事件,此时在 touchend
事件中取得 end_point
的坐标即获得了手指离开屏幕的坐标。按此思路下 代码应该修改以下:
... this.bind('touchstart',function(e){ var touch = e.touches[0]; start_piont = { x: touch.pageX, y: touch.pageY } }).bind('touchmove',function(e){ //在这里不断刷新 var touch = e.touches[0]; end_point = { x: touch.pageX, y: touch.pageY } }).bind('touchend', function(e){ //在这里取值计算 delta_x = end_point.x - start_piont.x; delta_y = end_point.y - start_piont.y; if(delta_x < -sensibility) { // 向左划动 direction.horizontal='left'; } else if (delta_x > sensibility) { // 向右划动 direction.horizontal='right'; } if(delta_y > sensibility){ direction.vertical = 'down'; }else if(delta_y < (-sensibility)){ direction.vertical = 'up'; } if(typeof callback === 'function'){ callback(direction); } //还原状态 direction = { horizontal : null, vertical : null } });
这样作确实能解决上面咱们提到的问题。可是从性能上来说在手指划动的时候不断的赋值或者计算绝对不是实现这个需求的最佳方案,虽然在这里并不会有明显的性能问题,可是原则上咱们仍是不要这样作。因此咱们采用下面的方案。 在引出下面方案以前咱们先来介绍一下 changedTouches
--‘触发事件时改变的触摸点的集合’,划动的点天然符合这个条件,也就是说咱们能够经过它来获取到手指离开屏幕时的那个点。不过这里要声明一下,这篇文章不涉及多点触摸的实现,因此咱们以单点(一根手指)的触摸为前提继续下面的代码改造:
this.bind('touchstart',function(e){ var touch = e.touches[0]; start_piont = { x: touch.pageX, y: touch.pageY } }).bind('touchmove',function(e){ // 请注意这里代码清空了 }).bind('touchend', function(e){ //经过 changedTouches 获取手指离开屏幕时的坐标 var touch = e.changedTouches[0]; end_point = { x: touch.pageX, y: touch.pageY } delta_x = end_point.x - start_piont.x; delta_y = end_point.y - start_piont.y; if(delta_x < -sensibility) { // 向左划动 direction.horizontal='left'; } else if (delta_x > sensibility) { // 向右划动 direction.horizontal='right'; } if(delta_y > sensibility){ direction.vertical = 'down'; }else if(delta_y < (-sensibility)){ direction.vertical = 'up'; } if(typeof callback === 'function'){ callback(direction); } //还原状态 direction = { horizontal : null, vertical : null } });
到这里咱们的组件也算基本编写完成了。调用方式以下
<!--引入依赖--> <script src="http://cdn.bootcss.com/zepto/1.2.0/zepto.min.js"></script> <!--引入组件--> <script src="../plugins/cj-zepto-gesture.min.js"></script>
调用方法:
//使用案例,90 为灵敏度 $('.test-container').gesture(function(direction){ //回调函数处理 console.log('拓展方法',direction); },90);
test-container
为手势操做容器,以下面的蓝色 div、
限于笔者技术,文章观点不免有不当之处,但愿发现问题的朋友帮忙指正,笔者将会及时更新。也请转载的朋友注明文章出处并附上原文连接,以便读者能及时获取到文章更新后的内容,以避免误导读者。笔者力求避免写些晦涩难懂的文章(虽然也有人说这样显得高逼格,专业),尽可能使用简单的用词和例子来帮助理解。若是表达上有好的建议的话也但愿朋友们在评论处指出。
本文为做者原创,转载请注明出处! Cboyce