JS案例 - 基于vue的移动端长按手势
================================惯例碎碎念前言================================
当时首先想到要做长按事件的时候,我想到的是vue内部的自定义指令,毕竟官网里边有这么一句描述:
有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
但是项目用在app中,因为另一个未知原因的bug,自定义事件躺枪(至今死不瞑目)。长按事件被我改成了在初始化时,就直接绑定到需要他的dom上。
================================正经文================================
绑定的命令写在mounted钩子里,这是因为在created内部我也找不到dom,而在mounted阶段,所有的dom结构和数据都被展示到页面当中,
详情可以参见“vue生命周期强刷”:https://www.cnblogs.com/padding1015/p/9159381.html
所以,下边的代码经过不断的轮回,最终是写在mounted里边的
let oDiv = document.getElementById('canvas');
// 因为长按事件要加在div#canvas上的,如果事件是任何地方的话,就是document
1 oDiv.addEventListener("touchstart", function(e) {}, false); 2 3 oDiv.addEventListener("touchmove", function(e) {}, false); 4 5 oDiv.addEventListener("touchend", function(e) {}, false);
至于,三个绑定事件的回调里再写什么,就不太关vue的事了(dui,就是这么草率)。
在公布最终的成型代码之前,容我先补一波关于事件的”兵”。
主要兵线呢有三条:
中路:dom2级事件处理程序
上路:事件流
下路:触摸事件
所需事件知识点分布如下图:
(查看大图:右键新标签中打开)掌握了上边的知识点后,就是在长按功能里边的应用了。
别急~
长按功能原理分析一波:
所谓的长按其实就是手指按下去,不移动,超过一定时间才把手指拿开的一个过程(我说的好有道理哈哈哈。然后听到一片同一个声音:废话!!)。
而在这个过程中,正好是触摸的三个事件。
监听手指按下去后是否有移动,就该touches上场了,监听他的clientX,clientY只要没变就是没移动。
并且在这个过程中,还会不时地有地方的英雄冒出来干扰我们补兵。那就是一个手机自带的效果了:
长按时,在移动端触摸文字,(至少ios里)会出现选择文字等干扰我们的真正功能,用了preventDefault()这个属性后就没有了。
纸上谈兵是没用的额,直接上长按功能代码:
为了不必要的麻烦(其实就是我懒得写),把多余的其他代码删掉了,所以不要惊讶export default里边为什么只有mounted。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <script> let x = 0, y = 0, z = 0, timer1 = null ; export default { mounted(){ let oDiv = document.getElementById( 'canvas' ); oDiv.addEventListener( "touchstart" , function (e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false ; if (e.touches.length > 1) { return false ; } z = 0; timer1 = setTimeout( function () { z = 1; }, 500); x = e.touches[0].clientX; y = e.touches[0].clientY; }, false ); oDiv.addEventListener( "touchmove" , function (e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false ; if (x != e.touches[0].clientX || y != e.touches[0].clientY) { clearTimeout(timer1); return false ; } }, false ); oDiv.addEventListener( "touchend" , function (e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false ; if (z != 1) { clearTimeout(timer1); x = 0; y = 0; return false ; } else if (z=1){ x = 0; y = 0; z = 0; /* 到这里已确定触发了长按事件,接下来执行长按后要做的其他事情 */ } }, false ); } } </script> |
哎呀,我怎么可能直接甩了代码扭头就走呢!接下来请看~
讲解版本的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | <script> let x = 0, //用于记录clientX y = 0, //用于记录clientY z = 0, //用于判断,是否是已按住并超过了设定时间。 timer1 = null ; //用于定时器 export default { mounted(){ let oDiv = document.getElementById( 'canvas' ); // 因为长按事件要加在div#canvas上的,如果事件是任何地方的话,就是document /* 添加touchstart,手指触摸事件 */ oDiv.addEventListener( "touchstart" , function (e) { /* 阻止默认事件,其实这里ie的兼容写法returnValue没必要*/ if (e.preventDefault) e.preventDefault(); else e.returnValue = false ; /* 移动时触发touchmove导致多个touches对象,所以可以直接跳出*/ if (e.touches.length > 1) { return false ; } /* 这里有历史渊源的,是第二次点击的时候,把z的值还原。*/ z = 0; /* 手指一旦触摸屏幕,就开启一个倒计时定时器timer1 */ timer1 = setTimeout( function () { z = 1; // 如果倒计时结束还没有清楚定时器的话,就把z赋值为1,这样,当判断z=1就说明按住屏幕的时间达到了开发者设定的长按时间。也就是满足了长按事件 }, 500); /* 手指一旦触摸屏幕,要立即做的第二件事:记录触摸时的点的位置,并存在x,y两个变量里*/ x = e.touches[0].clientX; y = e.touches[0].clientY; }, false ); /* 绑定第二个事件touchmove,手指在屏幕上连续滑动时连续地触发*/ oDiv.addEventListener( "touchmove" , function (e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false ; /*判断,因为每次手指移动会连续触发touchmove,也就会不停的往event事件对象里边添加“跟踪触摸属性touches”*/ //这个属性是一个数组,每次新添加的都会在最前边。所以每次获取数组里边的第一个对象对应的clientX和clientY,就是实时的移动点的位置 //找这个点的作用,就是为了监听用户,是否按住还移动了。如果移动了,那不能算长按事件(不过这个也看产品需求,如果按住也要触发长按规定那个逻辑的话,这不要判断) if (x != e.touches[0].clientX || y != e.touches[0].clientY) { // 具体的判断方法,还记得touchstar那里已经记录了'起跑点'了,只要和x,y的值进行比较,与两个的值有任意一个不等,就是移动了。 // 那么移动的话,先要清除事先埋伏的定时器timer1.要不然,虽然不是长按事件但是倒计时还在进行中。 clearTimeout(timer1); return false ; //除掉'后患'后,安心的结束本次用户的触摸事件监听。 } }, false ); /* 添加第三个触摸事件touchend,这个事件的场景就是用户手指从屏幕拿开时触发*/ oDiv.addEventListener( "touchend" , function (e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false ; /* 上文已经介绍,当用户拿开的时候,只要判断用户从点击到拿开的时间。而记录时间长度比较麻烦,所以当时用了定时器,设定了一个我们想要的时间,时间到了就改变一个状态值z,所以这里我们只要判断z是否被改变即可*/ if (z != 1) { /* 如果用户手指头拿开的时候,z还是0,即不等于1,说明定时器还没被触发,也就说明没有达到长按的时间,那么不用期待了,同touchmove寿终正寝时要做的一样,清除定时器即可。*/ clearTimeout(timer1); /* 但是,touchend和touchmove还有一点不一样的,touchend是end(废话),touchmove不管move多少次,最终手指总要从屏幕拿开的,这就是他的关键点。而拿开就是结束,结束,是整个这次触摸生命过程的结束。*/ /* 所以,end还要做的事是初始化,是还原。他需要把x,y的值归为原始。以为了下次再次触发touchstart时做准备。好感人的故事。*/ x = 0; y = 0; return false ; //然后安心的“死去”。 } else if (z=1){ /* 如果,触发了长按事件,终于触发了长按事件!*/ /* 他还是不能得意忘形,在大展宏图之前,还是要做点准备,打扫一下战场,将各个变量初始化 */ x = 0; y = 0; z = 0; /* 然后,他才能开心的做自己计划已久的事业:执行长按后要做的其他事情 */ /* 是什么事情呢? 五点半了,吃饭。*/ } }, false ); } } </script> |
奥,对了还有个现象,在与vue中的swiper一同食用时,长按住并且滑动会触发上/下翻页。如果touchmove里边还要有什么动作的话,加上swiper体验很不好。
2018-07-07 17:35:31
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
2017-07-07 Sass控制命令及函数知识整理
2017-07-07 SASS环境搭建及HBuilder中sass预编译配置