================================ 惯例碎碎念前言 ================================
当时首先想到要做长按事件的时候, 我想到的是 vue 内部的自定义指令, 毕竟官网里边有这么一句描述:
有的情况下, 你仍然需要对普通 DOM 元素进行底层操作, 这时候就会用到自定义指令 https://cn.vuejs.org/v2/guide/custom-directive.html .
但是项目用在 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
- oDiv.addEventListener("touchstart", function(e) {}, false);
- oDiv.addEventListener("touchmove", function(e) {}, false);
- oDiv.addEventListener("touchend", function(e) {}, false);
至于, 三个绑定事件的回调里再写什么, 就不太关 vue 的事了 (dui, 就是这么草率).
在公布最终的成型代码之前, 容我先补一波关于事件的 "兵".
主要兵线呢有三条:
中路: dom2 级事件处理程序
上路: 事件流
下路: 触摸事件
所需事件知识点分布如下图:
(查看大图: 右键新标签中打开) 掌握了上边的知识点后, 就是在长按功能里边的应用了.
别急~
长按功能原理分析一波:
所谓的长按其实就是手指按下去, 不移动, 超过一定时间才把手指拿开的一个过程 (我说的好有道理哈哈哈. 然后听到一片同一个声音: 废话!!).
而在这个过程中, 正好是触摸的三个事件.
监听手指按下去后是否有移动, 就该 touches 上场了, 监听他的 clientX,clientY 只要没变就是没移动.
并且在这个过程中, 还会不时地有地方的英雄冒出来干扰我们补兵. 那就是一个手机自带的效果了:
长按时, 在移动端触摸文字,(至少 ios 里) 会出现选择文字等干扰我们的真正功能, 用了 preventDefault() 这个属性后就没有了.
纸上谈兵是没用的额, 直接上长按功能代码:
为了不必要的麻烦 (其实就是我懒得写), 把多余的其他代码删掉了, 所以不要惊讶 export default 里边为什么只有 mounted.
- <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>
哎呀, 我怎么可能直接甩了代码扭头就走呢! 接下来请看~
讲解版本的代码
- <script>
- let x = 0,// 用于记录 clientX
- y = 0,// 用于记录 clientX
- 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
来源: https://www.cnblogs.com/padding1015/p/9277922.html