Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multitouch panning not working correctly #767

Open
demrks opened this issue Mar 2, 2015 · 12 comments
Open

Multitouch panning not working correctly #767

demrks opened this issue Mar 2, 2015 · 12 comments

Comments

@demrks
Copy link

demrks commented Mar 2, 2015

I was trying to implement multitouch panning with hammer.js, so that you can start panning with one finger and continue with another one.

I thought that the deltaXY values already took care of that, but apparently the values of both deltaX and deltaY are sometimes jumping from one value to another. So if you do:

*Left finger down, Left finger pan (working), Right finger down (Left finger is still down), Right finger pan, Right finger up, Right finger down, Right finger pan etc. *

things get really weird. Similar things happen if the number of touch points change (e.g. pinch and pan at the same time), but this is probably related to #596.

I even tried to set mc.set({ pointers: 1 }); (no multitouch panning), but the second touch/pointer sometimes still fires 'panmove/panstart' events. In either case the deltaX/deltaY values are jumping randomly.

'panstart' in general is not really reliable currently. Sometimes it gets fired for a second touch, sometimes it doesn't.

@demrks
Copy link
Author

demrks commented Mar 12, 2015

I made some further tests to find the best way to calculate better deltaXY values... I'm not quite sure how those values are calculated right now, but I found that the easiest and most reliable way is by taking the average of all touchmovements in touchmove. I made a quick test to calculate deltaX and it works perfectly without any jumping. Since it takes the average it will theoretically work with any number of fingers the same way...

var lastX = [],
    currentX = [],
    deltaX = 0;

ELEMENT.addEventListener( 'touchstart', function( e ) {
    var i, idx;

    // Set default x/y values for new touches
    for( i = 0; i < e.changedTouches.length; i++ ) {
        idx = e.changedTouches[ i ].identifier;

        lastX[ idx ] = currentX[ idx ] = e.changedTouches[ i ].clientX;
    }
});

ELEMENT.addEventListener( 'touchmove', function( e ) {
    var i, idx, averageDeltaX = 0;

    // Loop through all active touches to calculate average delta
    for( i = 0; i < e.touches.length; i++ ) {
        idx = e.touches[ i ].identifier;

        currentX[ idx ] = e.touches[ i ].clientX;

        averageDeltaX += currentX[ idx ] - lastX[ idx ];

        lastX[ idx ] = currentX[ idx ];
    }

    averageDeltaX = averageDeltaX / e.touches.length;

    // deltaX is supposed to be e.deltaX in hammer.js
    deltaX += averageDeltaX;
});

ELEMENT.addEventListener( 'touchend', function( e ) {
    // Reset deltaX for current session, if there are no more touches left
    if ( e.touches.length === 0 ) {
        deltaX = 0;
    }
});

deltaY would work the same way obviously...

@sfrdmn
Copy link

sfrdmn commented Jun 9, 2015

On v2.0.3 I'm seeing panstart being fired consistently on multitouch pan when I go from two touch pointers to one. The behavior I would expect, though, is that panstart not be fired unless the number of pointers go from 0 to > 0 (and, of course, a pan is initiated), i.e. not until after all touch inputs have been removed

Maybe this is the same issue you saw, @demrks?

@demrks
Copy link
Author

demrks commented Jun 9, 2015

@sfrdmn Yes... I expected the same. It should always be:
panstart (once), panmove (multiple times), panend (once)
and not
panstart, panmove (multiple times)..., panstart, panmove (multiple times)..., ..., panend

This and some other problems (see above) made it pretty much impossible for me to create a very simple gallery (pan, zoom in etc.) with hammer.js. Single touch works just fine, but as soon as the touch/pointer count changes (one finger pan -> pinch etc.) both the deltaXY values and the events (e.g. panstart) are pretty unreliable.

Unfortunately I didn't see any progress with hammer.js, so I'm not sure if this will ever get fixed. In the end I had to write a gesture detection library myself. I took most of the API and event object names though from hammer.js and can only hope that someone takes over this library.

@arschmitz
Copy link
Contributor

@demrks development did stall for a while but i have taken over leading the project and we are putting together a team and development is starting again.

@shaunc
Copy link

shaunc commented Sep 14, 2015

I am trying to simulate a scrollable area so I can lazily render a large area. In your examples, two-fingered swipe on my mac (yosemite) using chrome is not detected. Is that related to this issue or is it just how the examples are configured? Is there an example out there that detects two-finger (horizontal) swipe, as that is default gesture for scroll on mac?

@runspired
Copy link
Contributor

A lot of this was likely cleaned up by the fixes to pan event ordering and touch ordering, we should revisit both the issues mentioned here for 2.0.6 or 2.1 but I think we should punt for 2.0.5

@runspired
Copy link
Contributor

^ @arschmitz

@dancek
Copy link

dancek commented Nov 28, 2015

I just tried to implement a horizontal swipe handler that does different things based on the number of fingers used. I'm using https://github.com/RyanMullins/angular-hammer and testing on an LG G2, and it looks like pan and swipe events always have exactly one pointer. I tried setting {pointers:2} and such, which just causes no events to be registered ever. I do get pinch events with two or three pointers.

Not sure where the issue is, and I'm not too keen to debug this further, but just FYI.

@daniel-wer
Copy link

In reply to @dancek and to save someone else the 30 minutes I just wasted on this:
To implement a pan/swipe handler with more than one finger, you need to set the option { pointers: 2 } for the pan/swipe gesture AND use the recognizeWith function (http://hammerjs.github.io/recognize-with/) to tell hammer that it is ok to recognize the pinch AND pan/swipe gesture together, otherwise it'll recognize the pinch gesture and drop pan/swipe.
The recognizeWith should only be necessary if you enabled the pinch gesture of course.

@michael-ts
Copy link

michael-ts commented Feb 18, 2018

@daniel-wer If I set up two pan handlers (pointers=1 and pointers=2) and one pinch, the first pan handler is ignored, even when I try to recognizeWith it against the second one. At one point it was actually working to swipe with two fingers, and pinch to zoom, but in tweaking I somehow lost that ability and a two finger pan seems to only fire the pinch event.

@bes
Copy link

bes commented May 2, 2018

@daniel-wer
After pounding my head against the wall with this for a couple of hours I was finally able to solve it.

For anyone that runs into the same problem:

If you REALLY want two different, simultaneous, pan handlers you must rename the event, otherwise it will overwrite the first handler when you add the second (Check the code for HammerManager.add)

N.B: I'm using TypeScript.

So something like this:

const pan1 = new Hammer.Pan({event: "pan1", direction: Hammer.DIRECTION_ALL, pointers: 1, threshold: 0});
const pan2 = new Hammer.Pan({event: "pan2", direction: Hammer.DIRECTION_ALL, pointers: 2, threshold: 0});

hammerManager.add(pan1);
hammerManager.add(pan2);

hammerManager.on("pan1", (e) => ...
hammerManager.on("pan2", (e) => ...

But in the end I just used one pan handler with pointers: 0, because that fit well with my use case.

const pan = new Hammer.Pan({event: "pan2", direction: Hammer.DIRECTION_ALL, pointers: 0, threshold: 0});

@karljacuncha
Copy link

@bes confirmed.
I had the similar issue to @demrks with the 2 finger panning getting 'random' deltas.
I wanted to have a flexible pan & pinch, so I ended up with:

        const mc = new Hammer.Manager( element );
        const oneFingerPan = new Hammer.Pan({
            event: 'oneFingerPan',
            direction: Hammer.DIRECTION_ALL,
            pointers: 1,
        });
       // similar, but 2 pointers & different event name:
        const twoFingerPan = new Hammer.Pan({
            event: 'twoFingerPan',
            direction: Hammer.DIRECTION_ALL,
            pointers: 2,
        });
        const pinch = new Hammer.Pinch({
            event: 'pinch',
        });
       // NOTE: recognize the pinch & 2 finger pan together
        twoFingerPan.recognizeWith(pinch);
        pinch.recognizeWith(twoFingerPan);
        
        mc.add([oneFingerPan, twoFingerPan, pinch]);

       // handler for single touch event:
        mc.on('oneFingerPan', (e) => {
            this.lastDeltaX = e.deltaX;
            this.lastDeltaY = e.deltaY;
            this.draw();
            if (e.isFinal) {
                this.updateState();
            }
        });

       /// handler for the mutli-touch events:
        mc.on('twoFingerPan pinch', (e) => {
            this.lastDeltaX = e.deltaX;
            this.lastDeltaY = e.deltaY;
            this.lastScale = this.state.scale * e.scale;
            this.draw();
            if (e.isFinal) {
                this.updateState();
            }
        });
``

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants