jQuery(Swipe vs. Touch)pageX和pageY始终返回0

18

我在iPhone上使用touchstart和touchend事件进行开发。我创建了一个示例页面,其中包含一个div元素,如果您触摸它并滚动页面,则应返回起始和结束位置的y坐标。

链接:http://jsbin.com/ibemom/2

jQuery代码:

var touchStartPos;

$('.theDiv')
  .bind('touchstart', function(e){
    touchStartPos = e.pageY;
  })
  .bind('touchend', function(e){
    alert(touchStartPos + ' | ' + e.pageY)
  })   

然而,当我在iPhone上加载该页面时,警报仍会将两个值都报告为零。有人看出我写错了什么吗?

更新:

我偶然发现了jQuery Mobile项目中的这段代码:https://github.com/jquery/jquery-mobile/issues/734

其中一个评论引起了我的注意:

 // (in iOS event.pageX and event.pageY are always 0 )

它没有说明为什么会这样,但至少我找到了另一个看到同样情况的人。将在那个页面上查看示例代码以查看是否有解决方案。

更新 II:

好的,在查看上述链接中的示例代码后,看起来这将返回实际值:

e.originalEvent.touches[0].pageY

问题是,我现在意识到这不是我需要的。它返回我在对象上附加事件时触摸区域相对于HTML文档的Y偏移量…而不是浏览器窗口。

我的目标是找出触摸开始和结束的屏幕位置,然后比较这些值。如果它们相差很大,那么我们就认为进行了滑动...而不是触摸。

更新三:

我还发现了这些示例的参考:

e.originalEvent.touches[0].pageX

event.targetTouches[0].pageY

尽管我似乎也无法使它们起作用。两者都返回未定义错误。

更新IV:

看起来一个解决方案是追踪文档本身的偏移量(offset())。我可以对文档应用touchend事件,然后检查它的偏移量(offset)。

问题在于我的touchend事件将首先触发页面上的元素,然后再触发文档上的元素(冒泡)。因此,在需要确定对象的touchend事件执行之前,我实际上无法确定它是触摸还是滑动。

仍在考虑这个问题...


奇怪的,不知道移动设备如何,但在点击方面运行良好 http://jsfiddle.net/nH2PU/ - Sinetheta
1
我希望这不会是一个很棘手的问题。 ;) - DA.
在Android(2.2.2)中似乎发生了同样的事情。 - NoBugs
4个回答

20

很遗憾,当手指离开屏幕(touchend)时是无法检测到触摸的。

我的第一个例子证明了这一点:http://jsfiddle.net/Y4fHD/

现在介绍一种解决方法,或者叫它别的名字也可以。也许不在touchend事件上检测触摸是有意义的,因为触摸已经结束了。

touchmove事件上绑定处理程序:http://jsfiddle.net/Y4fHD/1/

var endCoords = {};
$(document.body).bind("touchmove", function(event) {
    endCoords = event.originalEvent.targetTouches[0];
});

然后使用变量endCoords来确定最后的触摸点。

$(document.body).bind("touchend", function(event) {
    $('p').text("Your end coords is: x: " + endCoords.pageX + ", y: " + endCoords.pageY);
});

好的,试着轻触您的设备!然后错误仍然会发生:为什么?因为您没有移动您的触摸。

如果我们在touchstart中已经定义了endCoords变量,那么我们就可以解决这个问题:http://jsfiddle.net/Y4fHD/2/

var endCoords = {};
$(document.body).bind("touchstart touchmove", function(event) {
    endCoords = event.originalEvent.targetTouches[0];
});

然后使用变量endCoords来确定最后的触摸位置

$(document.body).bind("touchend", function(event) {
    $('p').text("Your end coords is: x: " + endCoords.pageX + ", y: " + endCoords.pageY);
});

现在尝试点击您的设备!

一些最终提示是:创建变量:startCoordsendCoords,然后在touchend事件中使用它们:http://jsfiddle.net/Y4fHD/3/

var startCoords = {}, endCoords = {};
$(document.body).bind("touchstart", function(event) {
    startCoords = endCoords = event.originalEvent.targetTouches[0];
});
$(document.body).bind("touchmove", function(event) {
    endCoords = event.originalEvent.targetTouches[0];
});
$(document.body).bind("touchend", function(event) {
    $('p').text("Your touch on the axis: " + Math.abs(startCoords.pageX-endCoords.pageX) + "x, " + Math.abs(startCoords.pageY-endCoords.pageY) + "y");
});

注意:
以上示例均未经过测试,希望能够正常工作!
Math.abs 可以给我一个数的绝对值,例如:-5 变成 5。


我发现在最后一个例子中,您在第"$('p').text("Your touch on the axis..."的行上两次写了Y轴的绝对值。 - Arabam
@Arabam,问题已经解决。想不到两年后那个类型错误终于被找出来了 :) - Andreas Louv
感谢您提到event.originalEvent - 在Android上,原始事件会抑制targetTouches属性并破坏一切!有趣的是,在iOS上这不是问题 - 这使得调试更加困难。 - jocull

2

上面NULL的答案对我非常有用,但是在touchstart绑定的函数中不能将startCoords分配给endCoords。它们指向同一个对象,因此当endCoords更新时,startCoords也会更新。当touchend事件触发时,startCoords始终等于endCoords。

下面是更新后的代码。这在iPad 3上使用iOS 6.0上的Safari上对我有效。还进行了编辑以修复数学和显示错误,并删除了touchend-bound函数中的Math.abs()。还在touchmove-bound函数中添加了一个event.preventDefault()调用,以便在iOS上的Google Chrome上也能正常工作。

var startCoords = {}, endCoords = {};

$(document.body).bind("touchstart", function(event) {
    endCoords = event.originalEvent.targetTouches[0];
    startCoords.pageX = event.originalEvent.targetTouches[0].pageX;
    startCoords.pageY = event.originalEvent.targetTouches[0].pageY;
});

$(document.body).bind("touchmove", function(event) {
    event.preventDefault();
    endCoords = event.originalEvent.targetTouches[0];
});

$(document.body).bind("touchend", function(event) {
    $('p').text("Your touch on the axis: " + (endCoords.pageX-startCoords.pageX) + "x, " + (endCoords.pageY-startCoords.pageY) + "y");
});

2

我使用的是简化版代码,用于检测.myelement的滑动手势,在原始代码中它是一个幻灯片画廊。

$(function() {
    var diff, tchs, del = 150,
    clk = function(el){
        if ( typeof(tchs) !== 'object' ) return; //we have nothing to do
        if ( (diff - tchs[tchs.length - 1].pageX) < 0 ) { //swipe right

        }
        else if ( (diff - tchs[tchs.length - 1].pageX) > 0 ) { //swipe left

        }
    };  
    $('.myelement').bind('touchstart touchmove', function(ev){
            var oev = ev.originalEvent, el = $(this);
            switch( ev.type.charAt(5) ){
                case 's': //touch start
                    diff = oev.pageX;
                    window.setTimeout(clk, del, el);
                break;
                case 'm': //touch move
                    tchs = oev.touches;
                break;
            }
    });
});

更新:顺便提一下,当我使用MobiOne版本2.0.0M2测试中心软件时,它将touchend页面X值放置在我期望的位置,因此这听起来是一个糟糕的实现,如果您没有快速访问真实设备,则可能很容易混淆。

更新2:好的,受到积极反馈的启发 :) 意识到有点复杂的版本,它允许检测向右、向左、向上和向下的滑动,但需要进行清理,但你已经明白了。

$(function () {
    var ftch, // first touch cache
    lck = Math.sin(Math.PI / 4); //lock value, sine of 45 deg configurable

    var defSwipeDir = function (el, tchs) { // need define swaping direction, for better UX
        if (typeof (tchs) !== 'object') return 0; // check if there was any movements since   first touch, if no return 0
        var ltch = tchs[tchs.length - 1], // last touch object
            dx = ltch.pageX - ftch.pageX,
            dy = ltch.pageY - ftch.pageY,
            hyp = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)),
            sin = dy / hyp,
            cos = dx / hyp,
            dir;

        if (Math.abs(cos) >= lck) { // left or right swape
            dir = cos > 0 ? 'r' : 'l';
        } else { // up or down
            dir = sin < 0 ? 'u' : 'd';
        }
        el.trigger('swipe', dir); // trigger custom swipe event
        return dir; // and return direction too
    }

    $('.myelementTouchDetection').on('touchstart touchmove swipe', function (ev, d) {
        var oev = ev.originalEvent,
            myelementTouchDetection = $(this),
            dir; // you may know swipes on move event too

        switch (ev.type) {
            case 'touchstart':
                ftch = oev;
                break;
            case 'touchmove':
                dir = defSwipeDir(myelementTouchDetection, oev.touches);
                return false // cancel default behaiviour
                break;
            case 'swipe':
                switch (d) {
                    case 'r': // swipe right
                        console.log('swipe right');
                        break;
                    case 'l': // swipe left
                        console.log('swipe left');
                        break;
                    case 'u': // swipe up
                        console.log('swipe up');
                        break;
                    case 'd': // swipe down
                        console.log('swipe down');
                        break;
                }
                break;
        }
    })
});

1

这是对我有效的方法...

$('.someClass').bind('touchmove',function(e){
  e.preventDefault();
  var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
  var elm = $(this).offset();
  var x = touch.pageX - elm.left;
  var y = touch.pageY - elm.top;
  if(x < $(this).width() && x > 0){
      if(y < $(this).height() && y > 0){
              //your code here
      }
     }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接