Javascript 检测触摸设备上的左/右滑动,但允许向上/向下滚动

Javascript 检测触摸设备上的左/右滑动,但允许向上/向下滚动,javascript,jquery,touch,swipe,Javascript,Jquery,Touch,Swipe,我需要检测并对左/右滑动做出反应,但我想让用户能够在同一个元素上滚动,因此只要他只向左/向右移动手指,最大上/下移动X像素,它就不应该滚动,但当他超过X时,它应该滚动 所以我所做的是: var startX, startY, $this = $(this); function touchmove(event) { var touches = event.originalEvent.touches; if (touches && touches.le

我需要检测并对左/右滑动做出反应,但我想让用户能够在同一个元素上滚动,因此只要他只向左/向右移动手指,最大上/下移动X像素,它就不应该滚动,但当他超过X时,它应该滚动

所以我所做的是:

var startX, startY, $this = $(this);
function touchmove(event) {
        var touches = event.originalEvent.touches;
        if (touches && touches.length) {
            var deltaX = touches[0].pageX - startX;
            var deltaY = touches[0].pageY - startY;
            if (Math.abs(deltaY) > 50) {
                $this.html('X: ' + deltaX + '<br> Y: ' + deltaY + '<br>TRUE');
                $this.unbind('touchmove', touchmove);
                return true;
            } else {
                $this.html('X: ' + deltaX + '<br> Y: ' + deltaY);
                event.preventDefault();
            }
        }
    }

    function touchstart(event) {
        var touches = event.originalEvent.touches;
        if (touches && touches.length) {
            startX = touches[0].pageX;
            startY = touches[0].pageY;
            $this.bind('touchmove', touchmove);
        }
        //event.preventDefault();
    }
var startX,startY,$this=$(this);
功能触摸移动(事件){
var touchs=event.originalEvent.touchs;
如果(接触和接触长度){
var deltaX=touchs[0]。pageX-startX;
var deltaY=touchs[0]。pageY-startY;
如果(数学绝对值(deltaY)>50){
$this.html('X:'+deltaX+'
Y:'+deltaY+'
TRUE'); $this.unbind('touchmove',touchmove); 返回true; }否则{ $this.html('X:'+deltaX+'
Y:'+deltaY); event.preventDefault(); } } } 功能触摸启动(事件){ var touchs=event.originalEvent.touchs; 如果(接触和接触长度){ startX=touchs[0].pageX; startY=touchs[0].pageY; $this.bind('touchmove',touchmove); } //event.preventDefault(); }
但我不恢复在“如果”情况下滚动的能力


谢谢你的提示。

我自己编写了触摸处理程序事件。也许这对你有帮助

它检查:

快速点击:“fc”

向左轻扫:“swl”

向右滑动:“swr”

向上滑动:“swu”

向下滑动:“社署”

每个检查都会初始化相应的事件。但您可以滚动并执行正常执行的任何其他操作。你只是有一些新的活动

您需要swl swr,我还建议使用fc(快速点击)进行点击事件。。。它比普通的点击要快得多

window.onload = function() {
    (function(d) {
        var
            ce = function(e, n) {
                var a = document.createEvent("CustomEvent");
                a.initCustomEvent(n, true, true, e.target);
                e.target.dispatchEvent(a);
                a = null;
                return false
            },
            nm = true,
            sp = {
                x: 0,
                y: 0
            },
            ep = {
                x: 0,
                y: 0
            },
            touch = {
                touchstart: function(e) {
                    sp = {
                        x: e.touches[0].pageX,
                        y: e.touches[0].pageY
                    }
                },
                touchmove: function(e) {
                    nm = false;
                    ep = {
                        x: e.touches[0].pageX,
                        y: e.touches[0].pageY
                    }
                },
                touchend: function(e) {
                    if (nm) {
                        ce(e, 'fc')
                    } else {
                        var x = ep.x - sp.x,
                            xr = Math.abs(x),
                            y = ep.y - sp.y,
                            yr = Math.abs(y);
                        if (Math.max(xr, yr) > 20) {
                            ce(e, (xr > yr ? (x < 0 ? 'swl' : 'swr') : (y < 0 ? 'swu' : 'swd')))
                        }
                    };
                    nm = true
                },
                touchcancel: function(e) {
                    nm = false
                }
            };
        for (var a in touch) {
            d.addEventListener(a, touch[a], false);
        }
    })(document);
    //EXAMPLE OF USE
    var h = function(e) {
        console.log(e.type, e)
    };
    document.body.addEventListener('fc', h, false); // 0-50ms vs 500ms with normal click
    document.body.addEventListener('swl', h, false);
    document.body.addEventListener('swr', h, false);
    document.body.addEventListener('swu', h, false);
    document.body.addEventListener('swd', h, false);
}
要处理多个元素和相同的函数。。。只需添加一个处理程序

所以如果你有

<ul id="ul"><li>1</li><li>2</li><li>3</li></ul>
同样适用于fc和swr


ios中存在一个bug:不要使用alert()。。它将执行2次。

所有这些代码都需要改进(就像您可以在触摸操作中找到的大多数代码一样)

在玩触摸事件时,请记住,用户有多个手指,触摸具有标识符,并且
触摸
列表表示表面上的所有当前触摸,即使是未移动的触摸

因此,这个过程相对简单:

  • ontouchstart:获取第一次更改的触摸(不是
    event.originalEvent.touchs
    属性,而是
    event.originalEvent.changedtouchs
    one)。使用
    event.originalEvent.changedtouchs[0]注册其标识符。标识符
    和要查找的触摸属性(
    pageX
    /
    pageY
    clientX
    /
    clientY
    DOMElement.getBoundingClientRect()方法结合使用)

  • ontouchmove:确保当前触摸在带有
    event.originalEvent.changedTouches.identifiedTouch(标识符)
    的changedTouches列表中。如果未返回任何内容,则表示用户移动了另一个触摸屏(而不是您正在寻找的触摸屏)。还可以注册触摸属性以查找和执行任何您想要的操作

  • ontouchend:同样,您必须确保当前触摸在changedTouches列表中。使用触摸属性执行此操作,最后放弃当前的触摸标识符

  • 如果你想做得更强壮,考虑多个触摸(不仅仅是一个)来观察。< /P> 有关TouchEvent、TouchList和Touch on的更多信息:

    接受的答案中有一个“bug”。如果您在Android上不使用Chrome,而是使用内置浏览器或“网络视图”(适用于html5混合应用程序),则不会检测到滑动

    我发现由于正常的滚动行为,事件没有触发。因此,在touchmove中添加“e.preventDefault();”可以修复它,也可以修复已接受答案中Eric Fuller的修复

    这是一个很好的剪断,但在移动网络应用程序或网站中,这可能会导致糟糕的滚动口吃,因为触摸事件会一直被观察到

    所以我决定建造一些新的东西。它不像有新的活动听众那样舒适,但它足以满足我的需要,而且性能良好

    function detectswipe(el,func) {
      swipe_det = new Object();
      swipe_det.sX = 0;
      swipe_det.sY = 0;
      swipe_det.eX = 0;
      swipe_det.eY = 0;
      var min_x = 20;  //min x swipe for horizontal swipe
      var max_x = 40;  //max x difference for vertical swipe
      var min_y = 40;  //min y swipe for vertical swipe
      var max_y = 50;  //max y difference for horizontal swipe
      var direc = "";
      ele = document.getElementById(el);
      ele.addEventListener('touchstart',function(e){
        var t = e.touches[0];
        swipe_det.sX = t.screenX; 
        swipe_det.sY = t.screenY;
      },false);
      ele.addEventListener('touchmove',function(e){
        e.preventDefault();
        var t = e.touches[0];
        swipe_det.eX = t.screenX; 
        swipe_det.eY = t.screenY;    
      },false);
      ele.addEventListener('touchend',function(e){
        //horizontal detection
        if ((((swipe_det.eX - min_x > swipe_det.sX) || (swipe_det.eX + min_x < swipe_det.sX)) && ((swipe_det.eY < swipe_det.sY + max_y) && (swipe_det.sY > swipe_det.eY - max_y)))) {
          if(swipe_det.eX > swipe_det.sX) direc = "r";
          else direc = "l";
        }
        //vertical detection
        if ((((swipe_det.eY - min_y > swipe_det.sY) || (swipe_det.eY + min_y < swipe_det.sY)) && ((swipe_det.eX < swipe_det.sX + max_x) && (swipe_det.sX > swipe_det.eX - max_x)))) {
          if(swipe_det.eY > swipe_det.sY) direc = "d";
          else direc = "u";
        }
    
        if (direc != "") {
          if(typeof func == 'function') func(el,direc);
        }
        direc = "";
      },false);  
    }
    
    myfunction(el,d) {
      alert("you swiped on element with id '"+el+"' to "+d+" direction");
    }
    
    如果检测到刷卡,将使用参数元素id和“l、r、u、d”(左、右、上、下)调用函数“myfunction”


    示例:

    在触摸仍在移动时检测左右移动

    这是通过保存最后一个位置和使用超时来完成的,用于在触摸移动停止后擦除最后一个位置

    var currentX;
    var lastX = 0;
    var lastT;
    $(document).bind('touchmove', function(e) {
        // If still moving clear last setTimeout
        clearTimeout(lastT);
    
        currentX = e.originalEvent.touches[0].clientX;
    
        // After stoping or first moving
        if(lastX == 0) {
            lastX = currentX;
        }
    
        if(currentX < lastX) {
            // Left
        } else if(currentX > lastX){
            // Right
        }
    
        // Save last position
        lastX = currentX;
    
        // Check if moving is done
        lastT = setTimeout(function() {
            lastX = 0;
        }, 100);
    });
    
    var-currentX;
    var-lastX=0;
    var lastT;
    $(文档).bind('touchmove',函数(e){
    //如果仍在移动,请清除上次设置超时
    清除超时(lastT);
    currentX=e.originalEvent.touchs[0].clientX;
    //回采或首次移动后
    如果(lastX==0){
    lastX=当前X;
    }
    如果(当前x<最后x){
    //左
    }否则如果(currentX>lastX){
    //对
    }
    //保存最后位置
    lastX=当前X;
    //检查移动是否完成
    lastT=setTimeout(函数(){
    lastX=0;
    }, 100);
    });
    
    受@cocco I的启发,创建了一个更好的(非最小化)版本:

    或者jquery风格

    $("article").on("gesture-down", function (e) { ... });
    

    谢谢@T.J.Crowder,添加了@Raphael:您在任何地方声明了吗?是的,在顶部添加了@T.J.Crowder这太复杂了,我无法理解:/另一个解决方案是使用swipeleft上的事件侦听器,swiperight等是专为低cpu设备设计的,因为它可以防止许多检查和计算,并且只使用您看到的短代码,无需添加第三方库,也不会覆盖设备的本机滚动系统,这可能会使我的解决方案不那么落后
    detectswipe('an_element_id',myfunction);
    
    detectswipe('an_other_element_id',my_other_function);
    
    var currentX;
    var lastX = 0;
    var lastT;
    $(document).bind('touchmove', function(e) {
        // If still moving clear last setTimeout
        clearTimeout(lastT);
    
        currentX = e.originalEvent.touches[0].clientX;
    
        // After stoping or first moving
        if(lastX == 0) {
            lastX = currentX;
        }
    
        if(currentX < lastX) {
            // Left
        } else if(currentX > lastX){
            // Right
        }
    
        // Save last position
        lastX = currentX;
    
        // Check if moving is done
        lastT = setTimeout(function() {
            lastX = 0;
        }, 100);
    });
    
    (function(d) {
        // based on original source: https://stackoverflow.com/a/17567696/334451
        var newEvent = function(e, name) {
            // This style is already deprecated but very well supported in real world: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/initCustomEvent
            // in future we want to use CustomEvent function: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
            var a = document.createEvent("CustomEvent");
            a.initCustomEvent(name, true, true, e.target);
            e.target.dispatchEvent(a);
            a = null;
            return false
        };
        var debug = false; // emit info to JS console for all touch events?
        var active = false; // flag to tell if touchend should complete the gesture
        var min_gesture_length = 20; // minimum gesture length in pixels
        var tolerance = 0.3; // value 0 means pixel perfect movement up or down/left or right is required, 0.5 or more means any diagonal will do, values between can be tweaked
    
        var sp = { x: 0, y: 0, px: 0, py: 0 }; // start point
        var ep = { x: 0, y: 0, px: 0, py: 0 }; // end point
        var touch = {
            touchstart: function(e) {
                active = true;
                t = e.touches[0];
                sp = { x: t.screenX, y: t.screenY, px: t.pageX, py: t.pageY };
                ep = sp; // make sure we have a sensible end poin in case next event is touchend
                debug && console.log("start", sp);
            },
            touchmove: function(e) {
                if (e.touches.length > 1) {
                    active = false;
                    debug && console.log("aborting gesture because multiple touches detected");
                    return;
                }
                t = e.touches[0];
                ep = { x: t.screenX, y: t.screenY, px: t.pageX, py: t.pageY };
                debug && console.log("move", ep, sp);
            },
            touchend: function(e) {
                if (!active)
                    return;
                debug && console.log("end", ep, sp);
                var dx = Math.abs(ep.x - sp.x);
                var dy = Math.abs(ep.y - sp.y);
    
                if (Math.max(dx, dy) < min_gesture_length) {
                    debug && console.log("ignoring short gesture");
                    return; // too short gesture, ignore
                }
    
                if (dy > dx && dx/dy < tolerance && Math.abs(sp.py - ep.py) > min_gesture_length) { // up or down, ignore if page scrolled with touch
                    newEvent(e, (ep.y - sp.y < 0 ? 'gesture-up' : 'gesture-down'));
                    //e.cancelable && e.preventDefault();
                }
                else if (dx > dy && dy/dx < tolerance && Math.abs(sp.px - ep.px) > min_gesture_length) { // left or right, ignore if page scrolled with touch
                    newEvent(e, (ep.x - sp.x < 0 ? 'gesture-left' : 'gesture-right'));
                    //e.cancelable && e.preventDefault();
                }
                else {
                    debug && console.log("ignoring diagonal gesture or scrolled content");
                }
                active = false;
            },
            touchcancel: function(e) {
                debug && console.log("cancelling gesture");
                active = false;
            }
        };
        for (var a in touch) {
            d.addEventListener(a, touch[a], false);
            // TODO: MSIE touch support: https://github.com/CamHenlin/TouchPolyfill
        }
    })(window.document);
    
    document.body.addEventListener('gesture-right', function (e) {  ... });
    
    $("article").on("gesture-down", function (e) { ... });