Search code examples
javascripthtmleventsdom-eventsmulti-touch

Does targetTouches track touches moving simultaneously on multiple elements?


Suppose I've got four fingers down: two fingers (1, 2) in element A and two (3, 4) in element B.

Suppose I then move finger 1 and finger 3.

I should be looking at a touchmove event which will contain changedTouches containing fingers 1 and 3.

However what will targetTouches contain? Will it contain 1 and 2, or 3 and 4, or all 4?

It seems like the only way to provide enough info about the fact that 1 and 2 are on the same element and 3 and 4 are both on a different element is to send two touchmove events.

I'm building a touch framework and I have been constructing a global datastructure that tracks all fingers and their targets so I do know what's going on but I was wondering if it was possible to skip doing this entirely if the targetTouches events could provide me all the information necessary.

I tested it with touchstart on my page:

touchstart

document.touchstart = function(evt) { console.log("touchstart", evt.targetTouches); }

I put four fingers down in the order I described above. Next up I'll try to see if I can find out what targetTouches contains in touchmove which is a more difficult task because of how much data it generates.


Solution

  • I will post my findings on iOS6.0 (iPhone 5), iOS5.1.1 (iPad 3), Nexus 7 on Android 4.2.1 (Chrome 18.0.1025469), and Firefox for Android 17 on the Nexus 7 below.

    Currently only have data from the iPhone 5. This is a lot of work.

    What I did was make two elements in the page and in the touchstart call preventDefault() to prevent scroll. Then I had touchmove count the length of its changedTouches and targetTouches buffers and display that in the DOM. Furthermore, I counted the rate at which the events came in. Here's a snippet of the code:

    // exposed is a variable which other code uses rAF to present a representation of it on the DOM so I can view it
    touchmove: function(evt) { 
        evt.preventDefault(); 
        var time = Date.now();
        var diff = time - exposed.touch_move_last_time; 
        if (!exposed.touch_move_last_time) exposed.touch_move_last_time = Date.now();
        if (!exposed.touch_move_rate) exposed.touch_move_rate = 0;
        exposed.touch_move_last_time = time;
        exposed.touch_move_rate += (diff - exposed.touch_move_rate)*0.01;
        exposed.touch_move_changedTouches_count = evt.changedTouches.length;
        exposed.touch_move_targetTouches_count = evt.targetTouches.length;
    

    When I move my finger around, I move it in a circle rather than a zig-zag because at the zig-zag points it can fire fewer events than the sample rate. Due to the low rate of convergence (this is to produce numbers that don't keep fluctuating) it takes about 5 seconds of continued rubbing to get the reading to stabilize on the sample rate.

    iOS6.0:

    Put down two fingers in one element and move one finger around:
    changedTouches size: 1 (the one that moves)
    targetTouches size: 2 (the two fingers on the element)
    sample rate: 16.66ms

    Put down two fingers in one element and move both around:
    changedTouches size: 2 (both are moving)
    targetTouches size: 2 (the two fingers on the element)
    sample rate: 16.66ms

    Put down one finger in each element and move one finger around:
    changedTouches size: 1 (the one that moves)
    targetTouches size: 1 (refers to element initially touched by the one that moves)
    sample rate: 16.66ms

    Put down one finger in each element and move both fingers around:
    changedTouches size: 1 (the one that moves)
    targetTouches size: 1 (refers to element initially touched by the one moving)
    sample rate: 8.29ms

    Put down one finger in first element and two fingers in second element:
    if moving one finger in the second element
    changedTouches size: 1
    targetTouches size: 2
    sample rate: 16.66ms

    if moving both fingers in the second element
    changedTouches size: 2
    targetTouches size: 2
    sample rate: 16.6ms (really hard to keep the first finger not moving)

    if moving the finger in the first element
    changedTouches size: 1
    targetTouches size: 1
    sample rate: 16.66ms

    if moving the finger in the first element and one finger in the second element
    changedTouches size: 1
    targetTouches size: fluctuates between 1 and 2
    sample rate: 8.29ms

    if moving all three fingers
    changedTouches size: fluctuates between 1 and 2
    targetTouches size: 2 sample rate: about 8.3ms

    Put down two fingers in each element and move one in each element around:
    changedTouches size: 1
    targetTouches size: 2
    sample rate: about 9ms

    It's pretty clear that all cases where the sample rate is ~120Hz I had at least two elements that had fingers moving in it, which definitely seems to indicate that it will fire a separate touchmove for each simultaneously touched element. I would bet that extending the test to 3 elements would see sample rates of 180Hz. Update: I tested 3 elements on the iPad and it does produce a 5.4ms reading.

    What is a little curious to me is why I always get 8.29ms rather than 8.33ms exactly. The 9ms reading is probably just because it is more processing than it can handle at full speed (since I've got the DOM updating every frame)

    Another Update:

    On Chrome on the Nexus 7 the changedTouches buffer always seems to be filled with all the touches because the webkitForce reading is always changing for every single touch all the time. It is also so slow that the sample rate reading gives zero useful information. But by changing the number of fingers present in the last ordered element on the page it appears that the touchmove events are also correctly being fired on a per-element basis.

    Firefox on Nexus 7 is a bit better at producing reasonable sample rate readings but the updating of the display is even more sluggish than Chrome. Until Firefox becomes less choppy I don't consider it worth the effort of getting code working correctly on it.