Hammer.js: Disable pan while scrolling?

Created on 18 Mar 2015  Â·  10Comments  Â·  Source: hammerjs/hammer.js

Seems to be a problem with mobile safari / ios webviews.

While scrolling vertically, if your finger drifts left/right, it will trigger pan on elements in the view.

What workarounds exist?

This is the only reference I could find: http://stackoverflow.com/questions/27193953/permit-horizontal-pan-events-only-if-the-gesture-is-not-already-scrolling-vertic

Most helpful comment

This solution is dead simple, but if all you want to do is to not activate a horizontal pan handler if the user is currently scrolling, and not scroll if the user is currently panning, then this worked well for me:

var isScrolling = false;

var galleryHammer = new Hammer(element, {
    recognizers: [
        [Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL }]
    ]
});

// Making the event listener passive means we don't get any delays between
// the user scrolling and the browser having checked if it should prevent
// that event
window.addEventListener('scroll', function() {
    isScrolling = true;
}, { passive: true });

window.addEventListener('touchend', function() {
    isScrolling = false;
});

galleryHammer.on('pan', function(event) {
    if (isScrolling) {
        return;
    }

    // Normal logic...
});

All 10 comments

+1, i'm also looking for this.

+1 Working on a solution now. I wonder why the "Lock Axis" property was removed in Hammer 2.x....

Here's the original open ticket: https://github.com/hammerjs/hammer.js/issues/633

You can check the ev.angle on panstart and set a flag when the angle is pointing left or right, then do the pan action on panmove and reset the flag on panend.

Example:

    // ...

    proto.swipestart = function(ev) {
            var angle = Math.abs(ev.angle);

            if (angle >= 90 && angle < 150)
                return;

            if (angle > 30 && angle < 90)
                return;

            this.moving = true;
            // ...
        };

        proto.swipemove = function(ev) {
            if (!this.moving)
                return;
            // ... change the marginLeft or translate to deltaX position
            // ...
        };

        proto.swipeend = function(ev) {
            if (!this.moving)
                return;

            this.moving = false;
            // ...
        };

A work around for this is to just store the starting position. Then you can set a threshold for vertical offset from the start position, and check this in your pan handler if its more then your threshold just return.

I'm going to close this as not a bug. If this workaround does not work or if you think there is a bug here please comment and we will reopen

I’m using Hammer in a React component and was able to pull this off this way:

const Hammertime = require('hammerjs');

// Grab a reference to Panel
const panel = this.refs.Panel;

// Create a Hammer manager
const mc = new Hammertime.Manager(panel);

// Create a recognizer
const Pan = new Hammertime.Pan();

// Add the recognizer
mc.add(Pan);

// Subscribe to events:

// If we detect a scroll before a panleft/panright, disable panning
mc.on('panstart', (event) => {
    if (event.additionalEvent === 'panup' || event.additionalEvent === 'pandown') {
        this.setState({isScrolling: true});
    }
});
// Reenable panning
mc.on('panend', () => this.setState({isScrolling: false}));
mc.on('pan', (event) => {
    if (!this.state.isScrolling) {
        // … do stuff
    }
});

This solution is dead simple, but if all you want to do is to not activate a horizontal pan handler if the user is currently scrolling, and not scroll if the user is currently panning, then this worked well for me:

var isScrolling = false;

var galleryHammer = new Hammer(element, {
    recognizers: [
        [Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL }]
    ]
});

// Making the event listener passive means we don't get any delays between
// the user scrolling and the browser having checked if it should prevent
// that event
window.addEventListener('scroll', function() {
    isScrolling = true;
}, { passive: true });

window.addEventListener('touchend', function() {
    isScrolling = false;
});

galleryHammer.on('pan', function(event) {
    if (isScrolling) {
        return;
    }

    // Normal logic...
});

Any valid solution for this

+1, i'm also looking for this.

This worked for me (the code comes from a class, and this.hm is simply an Hammer instance):

this.hm.on('panleft', function(e){ // ...and same for panright
   if(e.pointerType == 'touch' && (Math.abs(e.deltaY) > Math.abs(e.deltaX))){ return false; }
   // do stuff
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

terion-name picture terion-name  Â·  10Comments

mandawino picture mandawino  Â·  11Comments

julmot picture julmot  Â·  9Comments

anselmh picture anselmh  Â·  10Comments

priyankaviswan picture priyankaviswan  Â·  5Comments