web-dev-qa-db-fra.com

Détecter le balayage gauche/droit sur les périphériques tactiles, mais autoriser le défilement vers le haut/bas

Je dois détecter les glissades gauche/droite et y réagir, mais je veux donner à l'utilisateur la possibilité de faire défiler le même élément, aussi longtemps qu'il ne déplace son doigt que vers la gauche/droite avec un mouvement maximum de X pixels vers le haut/bas , il ne devrait pas défiler, mais quand il dépasse X, il devrait défiler.

Alors ce que j'ai fait c'est:

var startX, startY, $this = $(this);
function touchmove(event) {
        var touches = event.originalEvent.touches;
        if (touches && touches.length) {
            var deltaX = touches[0].pageX - startX;
            var deltaY = touches[0].pageY - startY;
            if (Math.abs(deltaY) > 50) {
                $this.html('X: ' + deltaX + '<br> Y: ' + deltaY + '<br>TRUE');
                $this.unbind('touchmove', touchmove);
                return true;
            } else {
                $this.html('X: ' + deltaX + '<br> Y: ' + deltaY);
                event.preventDefault();
            }
        }
    }

    function touchstart(event) {
        var touches = event.originalEvent.touches;
        if (touches && touches.length) {
            startX = touches[0].pageX;
            startY = touches[0].pageY;
            $this.bind('touchmove', touchmove);
        }
        //event.preventDefault();
    }

Mais je ne rétablis pas la possibilité de faire défiler dans le cas "si" ... 

Merci pour tous les conseils.

26
Raphael Jeger

J'ai écrit mon propre gestionnaire d'événements Touch.maybe cela vous aide

il vérifie:

clic rapide: 'fc'

glisser à gauche: 'swl'

glisser à droite: 'swr'

balayez vers le haut: 'swu'

glisser vers le bas: 'swd'

chaque vérification initialise son événement correspondant, mais vous pouvez faire défiler et faire ce que vous faites normalement. vous avez juste quelques nouveaux événements. 

vous avez besoin de swl swr, je suggère également d'utiliser fc (fastclick) pour les événements de clic ... c'est beaucoup plus rapide qu'un clic normal.

window.onload = function() {
    (function(d) {
        var
            ce = function(e, n) {
                var a = document.createEvent("CustomEvent");
                a.initCustomEvent(n, true, true, e.target);
                e.target.dispatchEvent(a);
                a = null;
                return false
            },
            nm = true,
            sp = {
                x: 0,
                y: 0
            },
            ep = {
                x: 0,
                y: 0
            },
            touch = {
                touchstart: function(e) {
                    sp = {
                        x: e.touches[0].pageX,
                        y: e.touches[0].pageY
                    }
                },
                touchmove: function(e) {
                    nm = false;
                    ep = {
                        x: e.touches[0].pageX,
                        y: e.touches[0].pageY
                    }
                },
                touchend: function(e) {
                    if (nm) {
                        ce(e, 'fc')
                    } else {
                        var x = ep.x - sp.x,
                            xr = Math.abs(x),
                            y = ep.y - sp.y,
                            yr = Math.abs(y);
                        if (Math.max(xr, yr) > 20) {
                            ce(e, (xr > yr ? (x < 0 ? 'swl' : 'swr') : (y < 0 ? 'swu' : 'swd')))
                        }
                    };
                    nm = true
                },
                touchcancel: function(e) {
                    nm = false
                }
            };
        for (var a in touch) {
            d.addEventListener(a, touch[a], false);
        }
    })(document);
    //EXAMPLE OF USE
    var h = function(e) {
        console.log(e.type, e)
    };
    document.body.addEventListener('fc', h, false); // 0-50ms vs 500ms with normal click
    document.body.addEventListener('swl', h, false);
    document.body.addEventListener('swr', h, false);
    document.body.addEventListener('swu', h, false);
    document.body.addEventListener('swd', h, false);
}

dans ce cas, h est mon gestionnaire pour chaque type d’événement et j’ajoute les gestionnaires au corps.

pour ce que je comprends votre question il vous suffit d'écrire

YOURELEMENT.addEventListener('swr',YOURSWIPERIGHTFUNCTION,false);
YOURELEMENT.addEventListener('swl',YOURSWIPELEFTFUNCTION,false);

pour gérer plusieurs éléments et la même fonction ... il suffit d'ajouter un gestionnaire.

donc si vous avez

<ul id="ul"><li>1</li><li>2</li><li>3</li></ul>

tu fais:

var deleteli=function(e){
    var li=e.target;
    console.log('deleting '+li.textContent);
}
document.getElementById('ul').addEventListener('swl',deleteli,false);

idem pour fc & swr

il y a un bogue dans ios: n'utilisez pas alert () .. il sera exécuté 2 fois.

44
cocco

Il y a un "bug" dans la réponse acceptée. Si vous n'utilisez pas Chrome sur Android mais le navigateur intégré ou une "Webview" (pour une application html5-hybrid-app) par exemple, le balayage n'est pas détecté.

J'ai découvert que l'événement ne se déclenchait pas à cause du comportement de défilement normal. Donc, en ajoutant "e.preventDefault ();" in touchmove résoudrait le problème ou le correctif d’Eric Fuller dans la réponse acceptée.

C'est une belle copie mais dans une application Web mobile ou un site Web, cela pourrait entraîner un mauvais bégaiement du défilement, car les événements tactiles sont observés en permanence.

J'ai donc décidé de construire quelque chose de nouveau. Ce n'est pas aussi confortable d’avoir de nouveaux auditeurs d’événements, mais c’est assez confortable pour mes besoins et sa performance.

function detectswipe(el,func) {
  swipe_det = new Object();
  swipe_det.sX = 0;
  swipe_det.sY = 0;
  swipe_det.eX = 0;
  swipe_det.eY = 0;
  var min_x = 20;  //min x swipe for horizontal swipe
  var max_x = 40;  //max x difference for vertical swipe
  var min_y = 40;  //min y swipe for vertical swipe
  var max_y = 50;  //max y difference for horizontal swipe
  var direc = "";
  ele = document.getElementById(el);
  ele.addEventListener('touchstart',function(e){
    var t = e.touches[0];
    swipe_det.sX = t.screenX; 
    swipe_det.sY = t.screenY;
  },false);
  ele.addEventListener('touchmove',function(e){
    e.preventDefault();
    var t = e.touches[0];
    swipe_det.eX = t.screenX; 
    swipe_det.eY = t.screenY;    
  },false);
  ele.addEventListener('touchend',function(e){
    //horizontal detection
    if ((((swipe_det.eX - min_x > swipe_det.sX) || (swipe_det.eX + min_x < swipe_det.sX)) && ((swipe_det.eY < swipe_det.sY + max_y) && (swipe_det.sY > swipe_det.eY - max_y)))) {
      if(swipe_det.eX > swipe_det.sX) direc = "r";
      else direc = "l";
    }
    //vertical detection
    if ((((swipe_det.eY - min_y > swipe_det.sY) || (swipe_det.eY + min_y < swipe_det.sY)) && ((swipe_det.eX < swipe_det.sX + max_x) && (swipe_det.sX > swipe_det.eX - max_x)))) {
      if(swipe_det.eY > swipe_det.sY) direc = "d";
      else direc = "u";
    }

    if (direc != "") {
      if(typeof func == 'function') func(el,direc);
    }
    direc = "";
  },false);  
}

myfunction(el,d) {
  alert("you swiped on element with id '"+el+"' to "+d+" direction");
}

Pour utiliser la fonction, utilisez-la simplement comme

detectswipe('an_element_id',myfunction);

detectswipe('an_other_element_id',my_other_function);

Si un balayage est détecté, la fonction "myfunction" est appelée avec le paramètre element-id et "l, r, u, d" (gauche, droite, haut, bas).

Exemple: http://jsfiddle.net/rvuayqeo/1/

6
EscapeNetscape

Tous ces codes doivent être améliorés (comme la plupart des codes que vous pouvez trouver sur la manipulation tactile).

Lorsque vous jouez avec un événement tactile, gardez à l'esprit que l'utilisateur a plus d'un doigt, qu'une touche a un identificateur et que la liste touches représente toutes les touches actuelles sur la surface, même les touches n'ayant pas déplacé.

Le processus est donc relativement simple:

  1. ontouchstart: obtient le premier contact modifié (pas la propriété event.originalEvent.touches, mais celle event.originalEvent.changedTouches). Enregistrez son identifiant avec event.originalEvent.changedTouches[0].identifier et les propriétés tactiles à rechercher (pageX/pageY ou clientX/clientY qui sont plutôt utiles en combinaison avec la méthode DOMElement.getBoundingClientRect());

  2. ontouchmove: assurez-vous que le contact actuel est dans la liste des nappes modifiées avec event.originalEvent.changedTouches.identifiedTouch( identifier ). S'il ne renvoie rien, cela signifie que l'utilisateur a déplacé une autre touche (pas celle que vous recherchez). Enregistrez également les propriétés tactiles à rechercher et à faire ce que vous voulez.

  3. ontouchend: encore une fois, vous devez être sûr que le contact actuel est dans la liste des nappes modifiées. Effectuez le travail avec les propriétés tactiles et finalement supprimez votre identifiant tactile actuel.

Si vous voulez le faire plus fort, considérez plusieurs touches (pas une seule) à observer.

Plus d'informations sur TouchEvent, TouchList et Touch sur: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Touch_events

3
Loops

Inspiré par @cocco, j'ai créé une meilleure version (non minimisée):

(function(d) {
    // based on original source: https://stackoverflow.com/a/17567696/334451
    var newEvent = function(e, name) {
        // This style is already deprecated but very well supported in real world: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/initCustomEvent
        // in future we want to use CustomEvent function: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
        var a = document.createEvent("CustomEvent");
        a.initCustomEvent(name, true, true, e.target);
        e.target.dispatchEvent(a);
        a = null;
        return false
    };
    var debug = false; // emit info to JS console for all touch events?
    var active = false; // flag to tell if touchend should complete the gesture
    var min_gesture_length = 20; // minimum gesture length in pixels
    var tolerance = 0.3; // value 0 means pixel perfect movement up or down/left or right is required, 0.5 or more means any diagonal will do, values between can be tweaked

    var sp = { x: 0, y: 0, px: 0, py: 0 }; // start point
    var ep = { x: 0, y: 0, px: 0, py: 0 }; // end point
    var touch = {
        touchstart: function(e) {
            active = true;
            t = e.touches[0];
            sp = { x: t.screenX, y: t.screenY, px: t.pageX, py: t.pageY };
            ep = sp; // make sure we have a sensible end poin in case next event is touchend
            debug && console.log("start", sp);
        },
        touchmove: function(e) {
            if (e.touches.length > 1) {
                active = false;
                debug && console.log("aborting gesture because multiple touches detected");
                return;
            }
            t = e.touches[0];
            ep = { x: t.screenX, y: t.screenY, px: t.pageX, py: t.pageY };
            debug && console.log("move", ep, sp);
        },
        touchend: function(e) {
            if (!active)
                return;
            debug && console.log("end", ep, sp);
            var dx = Math.abs(ep.x - sp.x);
            var dy = Math.abs(ep.y - sp.y);

            if (Math.max(dx, dy) < min_gesture_length) {
                debug && console.log("ignoring short gesture");
                return; // too short gesture, ignore
            }

            if (dy > dx && dx/dy < tolerance && Math.abs(sp.py - ep.py) > min_gesture_length) { // up or down, ignore if page scrolled with touch
                newEvent(e, (ep.y - sp.y < 0 ? 'gesture-up' : 'gesture-down'));
                //e.cancelable && e.preventDefault();
            }
            else if (dx > dy && dy/dx < tolerance && Math.abs(sp.px - ep.px) > min_gesture_length) { // left or right, ignore if page scrolled with touch
                newEvent(e, (ep.x - sp.x < 0 ? 'gesture-left' : 'gesture-right'));
                //e.cancelable && e.preventDefault();
            }
            else {
                debug && console.log("ignoring diagonal gesture or scrolled content");
            }
            active = false;
        },
        touchcancel: function(e) {
            debug && console.log("cancelling gesture");
            active = false;
        }
    };
    for (var a in touch) {
        d.addEventListener(a, touch[a], false);
        // TODO: MSIE touch support: https://github.com/CamHenlin/TouchPolyfill
    }
})(window.document);

Changements importants par rapport à la version originale de @cocco:

  • utilisez event.touches[0].screenX/screenY comme source principale d’informations. Les propriétés pageX/pageY ne représentent pas correctement le mouvement des contacts à l'écran car si un élément de page défile avec le toucher, cela affecte également les valeurs de pageX/pageY.
  • ajouter un paramètre de longueur minimale de geste
  • ajouter un paramètre de tolérance pour ignorer les gestes proches de la diagonale
  • ignorer le geste si le contenu de la page a défilé avec le geste (vérifiez la différence dans pageX/pageY avant de déclencher le geste)
  • abandonner le geste si plusieurs contacts sont effectués pendant le geste

Ce qu'il faudrait faire à l'avenir:

  • utilisez CustomEvent() function interface au lieu de la méthode createEvent().
  • ajouter la compatibilité MSIE
  • peut-être configurer la longueur minimale de geste pour pageX/pageY séparément de screenX/screenY?
  • Il semble que le défilement fileté de Chrome pose encore quelques problèmes de détection du défilement si le mouvement tactile est trop rapide. Peut-être attendez-vous l'image suivante avant de décider où le défilement s'est déroulé avant de décider si l'événement doit être déclenché?

L'utilisation est la suivante:

document.body.addEventListener('gesture-right', function (e) {  ... });

ou style jquery

$("article").on("gesture-down", function (e) { ... });
1
Mikko Rantalainen

Détection de gauche et de droite lorsque le toucher est toujours en mouvement .

Ceci est fait en sauvegardant la dernière position et en utilisant timeout pour effacer la dernière position après un arrêt tactile.

var currentX;
var lastX = 0;
var lastT;
$(document).bind('touchmove', function(e) {
    // If still moving clear last setTimeout
    clearTimeout(lastT);

    currentX = e.originalEvent.touches[0].clientX;

    // After stoping or first moving
    if(lastX == 0) {
        lastX = currentX;
    }

    if(currentX < lastX) {
        // Left
    } else if(currentX > lastX){
        // Right
    }

    // Save last position
    lastX = currentX;

    // Check if moving is done
    lastT = setTimeout(function() {
        lastX = 0;
    }, 100);
});
0
Nebojsa Sapic