开发者

Webkit iphone/ipad issue with mutl-touch

开发者 https://www.devze.com 2023-01-15 15:40 出处:网络
In a UIWebView (multi-touch enabled) I got a page with two divs (div1 and div2), each of them having registered for the touchstart and touchend events. Everytime they are receiving touch events, I am

In a UIWebView (multi-touch enabled) I got a page with two divs (div1 and div2), each of them having registered for the touchstart and touchend events. Everytime they are receiving touch events, I am dumping the content of:

  • event.touches: contains all current touches
  • event.targetTouches: contains touches relevant to the given target
  • event.changedTouches: contains touches having triggered the event

Consider the following scenario:

  • tap on div1: event.touches contains div1
  • without releasing div1, tap on div2: event.touches contains div1 and div2
  • release div2 but not div1: event.touches is empty, it should not as div1 is still being pressed . Also div1 received a touchend event as if it had been released
  • wait a while, still havi开发者_如何学编程ng div1 pressed, and it will receive a new touchstart event, which doesn't make sense as it has never been released.

So basically when releasing one finger, it acts like both fingers were removed. Am I missing something?


Thanks funkybro for your comment but unfortunately I can stil observe the same erroneous behavior when intercepting touch events at the document level. Here is a trace of what is happening:

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)

Edit: Here is a code sample, to run on Safari Mobile or inside your own UIWebView on device (not simulator).

<!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>


Yes I have found this too. Sounds like it should be a bug, but I'm not sure.

The most flexible way I have found to do all sorts of multi-touch goodness in iOs webkit is to capture touch events on the entire document, i.e. call document.addEventListener() for all varieties of touch event you're interested in.

Then use some tactic to figure out on which element the touch happened. You can:

  • Examine the touch's target property to get some info about the element. However (another possible bug?), you can't find out the element's ID from this property, only its class, which is no good for distinguishing between multiple elements using the same class. Examine the source of this JS virtual light table for iOs to see the this in action.

  • Compare the touch's pageX and pageY co-ordinates, to the dimensions and positions of each of the elements the touch could relate to (relative to the document). For example, using 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!
    }
    

Using this method, all touch actions behave entirely like you'd expect :)


I have a solution, but it is not optimal. Keep track of touch.identifier as touchstart events fire. When you get a touchend event setinterval of 500ms. If during the interval another touchend and a touchstart have fired, see if the touchstart has an extant touch identifier and you can determine which finger lifted.

When two fingers are down there is no way to determine at the time of the first touchend which finger has lifted. I've looked at the order of the changedTouches, I've checked a saved copy of the touchstart events. I've looked at the gesture events that precede the glitch. The delay in the remaining finger's duplicate touchstart depends on a touchmove firing.

If this bug is busting your multi-touch interaction, go add feedback to this clearly wrong description of the event sequence on the developer docs. GestureEvent Class Reference


It seems that this problem was fixed in iOS 4.2 (I could not reproduce it on an iPhone 4 with iOS 4.2.1)

0

精彩评论

暂无评论...
验证码 换一张
取 消