【问题标题】:Webkit iphone/ipad issue with mutl-touch多点触控的 Webkit iphone/ipad 问题
【发布时间】:2010-09-12 14:06:35
【问题描述】:

在 UIWebView(启用多点触控)中,我得到一个包含两个 div(div1 和 div2)的页面,每个 div 都注册了 touchstart 和 touchend 事件。每次他们收到触摸事件时,我都会转储以下内容:

  • event.touches:包含所有当前 触摸
  • event.targetTouches: 包含与 给定目标
  • event.changedTouches: 包含触发 活动

考虑以下场景:

  • 点击 div1:event.touches 包含 div1
  • 不释放 div1,点击 div2:event.touches 包含 div1 和 div2
  • 释放 div2 但不释放 div1:event.touches 是空的,它不应该因为 div1 仍然被按下。 div1 也收到了一个 touchend 事件,就好像它已经被释放了一样
  • 稍等片刻,仍然按下 div1,它会收到一个新的 touchstart 事件,这没有意义,因为它从未发布过。

所以基本上当松开一根手指时,它就像两根手指都被移开一样。我错过了什么吗?

【问题讨论】:

  • 刚刚在 iPod 上偶然发现了同样的问题。我使用 JavaScript,所以问题似乎相当基本(我们这边的错误或误解 ;-)。
  • 是的,我很惊讶遇到这样一个问题,因为这是一个相当大的问题,多点触控在这种情况下几乎无法使用。希望我们错过了什么......
  • 我想我遇到了同样的问题? stackoverflow.com/questions/3666929/…你能确认一下吗?
  • 来到这里是因为我有同样的问题,我在 iPad 上使用 PhoneGap/JavaScript。一个 div 的 ontouchend 似乎触发了该 div 加上另一个不相关的 div 的 ontouchend 事件。

标签: iphone ipad safari webkit multi-touch


【解决方案1】:

感谢 funkybro 的评论,但不幸的是,在文档级别拦截触摸事件时,我仍然可以观察到相同的错误行为。这是正在发生的事情的痕迹:

finger 1 touches elem1:
20:44:00.130 onTouchStart: 
    touches len=1 (elem1)
    changedTouches len=1 (elem1)

finger 2 touches elem2 (finger 1 still presses elem1 and has not been released):
20:44:01.066  onTouchStart: 
    touches len=2 (elem1,elem2)
    changedTouches len=1 (elem2)

finger 2 being released (finger 1 still presses elem1 and has not been released):
this is where things begin to go wrong: we receive two touchend events consecutively for
both elem1 and elem2,even though finger 1 is still holding on elem1 and has never released it.
Also the event.touches array is empty for both events, which is wrong since elem1 is still
being pressed.
20:44:08.241  onTouchEnd: touches len=0
              changedTouches len=1 (elem1)

20:44:08.251  onTouchEnd: touches len=0
              changedTouches len=1 (elem2)

after 4 seconds in the same position (finger 1 pressed on elem1, finger 2 released),
we receive a new touchstart event, as if the system wanted to undo the previous mistake
and put things back into a consistent state.   
20:44:12.511  onTouchStart: 
    touches len=1 (elem1)
    changedTouches len=1 (elem1)

now releasing finger 1 from elem1, we receive the touchend event
20:44:14.751  onTouchEnd: 
    touches len=0 
    changedTouches len=1 (elem1)

编辑: 这是一个代码示例,可在 Safari Mobile 或您自己的设备(不是模拟器)上的 UIWebView 中运行。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Multi-touch test</title>
    <style type="text/css">
        div.square {
            position:absolute;
            width:80px;
            height:80px;
            opacity:0.5;
        }
        div#elem1 {
            left:50px;
            top:50px;
            background-color:red;
        }
        div#elem2 {
            left:200px;
            top:50px;
            background-color:green;
        }
        textarea#logger {
            position:absolute;
            width100%;
            height:70%;
            top:30%;
            background-color:grey;
            color:white;
            overflow: scroll;
        }
    </style>

    <script type="text/javascript">
    function log(text) {
        logger.value = logger.value + text;
        logger.scrollTop = logger.scrollHeight;
    }

    function touchesDumpStr(touches,logPrefix) {
        var str = logPrefix + ', count=' + touches.length + ':';
        for (var i=0; i<touches.length; ++i) {
            if (typeof touches[i].target.customName != 'undefined') {
                str += touches[i].target.customName +' ';
            }
        }
        str += '\n';
        return str;
    }

    function onTouchStart(e) {
        log('onTouchStart\n');
        log(touchesDumpStr(e.touches, 'touches'));
        log(touchesDumpStr(e.targetTouches, 'targetTouches'));
        log(touchesDumpStr(e.changedTouches, 'changedTouches'));
        for (var i=0; i<e.changedTouches.length; ++i) {
            e.changedTouches[i].target.style.opacity=1.0;
        }
        e.preventDefault();
    }

    function onTouchEnd(e) {
        log('onTouchEnd\n');
        log(touchesDumpStr(e.touches, 'touches'));
        log(touchesDumpStr(e.targetTouches, 'targetTouches'));
        log(touchesDumpStr(e.changedTouches, 'changedTouches'));
        for (var i=0; i<e.changedTouches.length; ++i) {
            e.changedTouches[i].target.style.opacity=0.5;
        }
        e.preventDefault();
    }

    var logger;
    function init() {
        logger = document.getElementById('logger');
        document.getElementById('elem1').customName='elem1';
        document.getElementById('elem2').customName='elem2';
        document.addEventListener("touchstart", onTouchStart, false);
        document.addEventListener("touchend", onTouchEnd, false);   
    }
    </script>
</head>
<body onload="init();">
    <div class="square" id="elem1"></div>
    <div class="square" id="elem2"></div>
    <textarea id="logger" rows="10" cols="45" readonly></textarea>
</body>
</html>

【讨论】:

  • 理想情况下,这应该是对您原始答案的补充,或者是对我的评论的回应,而不是答案本身。
  • 我不太了解您的调试,但听起来您仍然为每个单独的元素附加了侦听器,这就是导致您观察到的行为的原因。您可以将实际代码的简短相关 sn-ps 粘贴到您的问题中吗?
  • 在上面的代码示例中,我还尝试覆盖一个透明的 div 覆盖整个屏幕,以减少接收到的 touchend 事件的数量。但是只要一根手指被抬起,event.touches 就会被清空,就好像两根手指被抬起一样......看起来像一个绝望的案例。
  • 我注意到的更多细节:在 2 个 touchend 事件触发并且 e.touches.length 为 0 之后。如果如您所描述的,手指 1 再次按下 elem1。 touch.identifier 和 elem1 上的第一次 touch 一样吗?我认为这一定是一个错误,因为它严重阻碍了多点触控功能的实用性!
【解决方案2】:

是的,我也发现了这个。听起来应该是一个错误,但我不确定。

我发现在 iOs webkit 中实现各种多点触控功能的最灵活的方法是在整个文档上捕获触控事件,即调用 document.addEventListener() 获取所有类型的触控事件你有兴趣。

然后使用一些策略来确定触摸发生在哪个元素上。你可以:

  • 检查触摸的target 属性以获取有关元素的一些信息。但是(另一个可能的错误?),您无法从该属性中找到元素的 ID,只能找到其类,这对于区分使用同一类的多个元素没有好处。检查this JS virtual light table for iOs 的来源以查看此操作。

  • 将触摸的pageXpageY 坐标与触摸可能相关的每个元素的尺寸和位置进行比较(相对于文档)。例如,使用 jQuery:

    var x = touch.pageX;
    var y = touch.pageY;
    var offset=$(element).offset();
    if (x >= offset.left && y >= offset.top && x < (offset.left + $(element).width()) && y < (offset.top + $(element).height())) 
    {
        // element was touched!
    }
    

使用此方法,所有触摸操作的行为都完全符合您的预期:)

【讨论】:

  • 如果我理解正确,这对我来说没有帮助:我想跟踪一个元素上的两个独立触摸。
  • 这将对您有所帮助;您可以在整个页面上跟踪尽可能多的触摸,跟踪尽可能多的元素。
  • 是的,但是正如我在另一个答案中提到的那样,一旦您抬起一根手指,您将不知道哪根手指已被释放,因为您的所有元素都会收到一个触摸端,即使是空触摸。如果这对于您想要使用多点触控并不重要,那么这很好,如果它确实重要,那么只要有这个错误就忘记 webkit。看起来您想跟踪同一个元素上的触摸,所以你应该没事。
  • 确实,所以你需要改变你的方法:将整个文档视为一个元素,不要在每个元素的基础上监听触摸。
【解决方案3】:

我有一个解决方案,但不是最优的。在 touchstart 事件触发时跟踪 touch.identifier。当你得到一个 500ms 的 touchend 事件 setinterval 时。如果在间隔期间另一个 touchend 和 touchstart 已触发,请查看 touchstart 是否具有现有的触摸标识符,您可以确定哪个手指抬起。

当两根手指向下时,无法确定在第一次触摸时哪根手指抬起。我查看了 changedTouches 的顺序,检查了 touchstart 事件的保存副本。我已经查看了故障之前的手势事件。剩余手指的重复触摸启动的延迟取决于 touchmove 触发。

如果此错误破坏了您的多点触控交互,请在开发人员文档中对这个明显错误的事件序列描述添加反馈。 GestureEvent Class Reference

【讨论】:

    【解决方案4】:

    这个问题似乎在 iOS 4.2 中已修复 (我无法在装有 iOS 4.2.1 的 iPhone 4 上重现它)

    【讨论】:

    • iOS 4.2 有所改进,但这并没有完全解决。根据按下的时间和项目数量,可能会出现滞后。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多