Detecta un deslizamiento de dedo a través de JavaScript en el iPhone y Android

268

¿Cómo puede detectar que un usuario deslizó su dedo en alguna dirección sobre una página web con JavaScript?

Me preguntaba si había una solución que funcionara para sitios web tanto en el iPhone como en un teléfono Android.

827
fuente
1
Para el reconocimiento de deslizamiento, recomendaría Hammer.js . Es bastante pequeño y admite muchos gestos: - Deslizar - Rotar - Pellizcar - Presionar (mantener presionado) - Tocar - Desplazar
Will Brickner
Hay un evento: "touchmove"
Clay
@Clay que todavía no funciona en Safari, así que no hay iPhone.
Jakuje

Respuestas:

342

Ejemplo simple de código JS de vainilla:

document.addEventListener('touchstart', handleTouchStart, false);        
document.addEventListener('touchmove', handleTouchMove, false);

var xDown = null;                                                        
var yDown = null;

function getTouches(evt) {
  return evt.touches ||             // browser API
         evt.originalEvent.touches; // jQuery
}                                                     

function handleTouchStart(evt) {
    const firstTouch = getTouches(evt)[0];                                      
    xDown = firstTouch.clientX;                                      
    yDown = firstTouch.clientY;                                      
};                                                

function handleTouchMove(evt) {
    if ( ! xDown || ! yDown ) {
        return;
    }

    var xUp = evt.touches[0].clientX;                                    
    var yUp = evt.touches[0].clientY;

    var xDiff = xDown - xUp;
    var yDiff = yDown - yUp;

    if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
        if ( xDiff > 0 ) {
            /* left swipe */ 
        } else {
            /* right swipe */
        }                       
    } else {
        if ( yDiff > 0 ) {
            /* up swipe */ 
        } else { 
            /* down swipe */
        }                                                                 
    }
    /* reset values */
    xDown = null;
    yDown = null;                                             
};

Probado en Android.

givanse
fuente
1
Parece fresco y simple, alguna idea de lo que es el soporte para este tipo de eventos touchstart, touchmove?
d.raev
1
Funciona bastante bien pero tiene problemas para detectar movimientos rectos. Publicaré otra respuesta en este tema que solucionó esto como solución JQuery (escritorio). También agrega la versión del mouse de estos eventos de deslizamiento y agrega una opción de sensibilidad.
Codebeat
1
Maldición. El tema está cerrado, así que no puedo agregar mi respuesta.
Codebeat
3
Esto funciona muy bien, pero izquierda / derecha y arriba / abajo están al revés.
Peter Eisentraut
44
originalEvent es una propiedad JQuery. Debe omitirse si ejecuta javascript puro sin JQuery. El código actual genera una excepción si se ejecuta sin JQuery.
Jan Derk
31

Basado en la respuesta de @ givanse, así es como podría hacerlo con classes:

class Swipe {
    constructor(element) {
        this.xDown = null;
        this.yDown = null;
        this.element = typeof(element) === 'string' ? document.querySelector(element) : element;

        this.element.addEventListener('touchstart', function(evt) {
            this.xDown = evt.touches[0].clientX;
            this.yDown = evt.touches[0].clientY;
        }.bind(this), false);

    }

    onLeft(callback) {
        this.onLeft = callback;

        return this;
    }

    onRight(callback) {
        this.onRight = callback;

        return this;
    }

    onUp(callback) {
        this.onUp = callback;

        return this;
    }

    onDown(callback) {
        this.onDown = callback;

        return this;
    }

    handleTouchMove(evt) {
        if ( ! this.xDown || ! this.yDown ) {
            return;
        }

        var xUp = evt.touches[0].clientX;
        var yUp = evt.touches[0].clientY;

        this.xDiff = this.xDown - xUp;
        this.yDiff = this.yDown - yUp;

        if ( Math.abs( this.xDiff ) > Math.abs( this.yDiff ) ) { // Most significant.
            if ( this.xDiff > 0 ) {
                this.onLeft();
            } else {
                this.onRight();
            }
        } else {
            if ( this.yDiff > 0 ) {
                this.onUp();
            } else {
                this.onDown();
            }
        }

        // Reset values.
        this.xDown = null;
        this.yDown = null;
    }

    run() {
        this.element.addEventListener('touchmove', function(evt) {
            this.handleTouchMove(evt).bind(this);
        }.bind(this), false);
    }
}

Puedes usarlo así:

// Use class to get element by string.
var swiper = new Swipe('#my-element');
swiper.onLeft(function() { alert('You swiped left.') });
swiper.run();

// Get the element yourself.
var swiper = new Swipe(document.getElementById('#my-element'));
swiper.onLeft(function() { alert('You swiped left.') });
swiper.run();

// One-liner.
(new Swipe('#my-element')).onLeft(function() { alert('You swiped left.') }).run();
Marwelln
fuente
77
este código probablemente no funcionará porque obtendrá una excepción al intentar llamar .binda indefinido porque en handleTouchMoverealidad no devolvió nada. También es inútil llamar al enlace cuando se llama a la función this.porque ya está vinculado al contexto actual
nick.skriabin
3
Me acabo de quitar .bind(this);y funcionó con gracia. gracias @nicholas_r
Ali Ghanavatian
Parte obtenga el elemento usted mismo, solo elimino '#' en document.getElementById ('my-element') y funcionó bien. Gracias @ Marwelln :)
Blue Tram
44
Si quieres que esperar hasta que los extremos de banda magnética (es decir, después de que se levante el dedo o onmouseup), el cambio touches[0]de changedTouches[0]y el tipo de controlador de eventos handleTouchMoveahandleTouchEnd
TetraDev
llama run()dos veces y obtienes una pérdida de memoria desagradable
Mat
20

Combiné algunas de las respuestas aquí en un script que usa CustomEvent para disparar eventos deslizados en el DOM. Agregue el script 0.7k swiped -events.min.js a su página y escuche los eventos swiped :

deslizado hacia la izquierda

document.addEventListener('swiped-left', function(e) {
    console.log(e.target); // the element that was swiped
});

deslizado hacia la derecha

document.addEventListener('swiped-right', function(e) {
    console.log(e.target); // the element that was swiped
});

arrebatado

document.addEventListener('swiped-up', function(e) {
    console.log(e.target); // the element that was swiped
});

deslizado hacia abajo

document.addEventListener('swiped-down', function(e) {
    console.log(e.target); // the element that was swiped
});

También puede adjuntar directamente a un elemento:

document.getElementById('myBox').addEventListener('swiped-down', function(e) {
    console.log(e.target); // the element that was swiped
});

Configuración opcional

Se pueden especificar los siguientes atributos para modificar cómo funciona la interacción magnética en su página (estos son opcionales) .

<div data-swipe-threshold="10"
     data-swipe-timeout="1000"
     data-swipe-ignore="false">
        Swiper, get swiping!
</div>

El código fuente está disponible en Github

John Doherty
fuente
Vine aquí porque pure-swipe no funcionaba para mí en MOBILE
StefanBob
@StefanBob si subes una marca en el repositorio de github con suficiente información para permitirme reproducir el problema, lo investigaré
John Doherty
1
Gracias, funciona perfectamente! Reemplacé Hammer.js con su biblioteca, porque el primero no funciona con el zoom del navegador y ese es un problema grave de usabilidad. Con esta biblioteca, el zoom funciona correctamente (probado en Android)
collimarco
15

lo que he usado antes es que tiene que detectar el evento de mousedown, registrar su ubicación x, y (lo que sea relevante) luego detectar el evento de mouseup y restar los dos valores.

holaandre
fuente
28
Creo que es touchstart, touchmove, touchcancel y touchchend con lo que uno trabajaría, no mousedown o mouseup.
Volomike
12

He encontrado que @givanse la respuesta brillante es la más confiable y compatible en múltiples navegadores móviles para registrar acciones de deslizamiento.

Sin embargo, hay un cambio en su código requerido para que funcione en los navegadores móviles modernos que están utilizando jQuery.

event.touchesno existirá si jQueryse usa y da como resultado undefinedy debe reemplazarse por event.originalEvent.touches. Sin jQuery, event.touchesdebería funcionar bien.

Entonces la solución se convierte,

document.addEventListener('touchstart', handleTouchStart, false);        
document.addEventListener('touchmove', handleTouchMove, false);

var xDown = null;                                                        
var yDown = null;                                                        

function handleTouchStart(evt) {                                         
    xDown = evt.originalEvent.touches[0].clientX;                                      
    yDown = evt.originalEvent.touches[0].clientY;                                      
};                                                

function handleTouchMove(evt) {
    if ( ! xDown || ! yDown ) {
        return;
    }

    var xUp = evt.originalEvent.touches[0].clientX;                                    
    var yUp = evt.originalEvent.touches[0].clientY;

    var xDiff = xDown - xUp;
    var yDiff = yDown - yUp;

    if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
        if ( xDiff > 0 ) {
            /* left swipe */ 
        } else {
            /* right swipe */
        }                       
    } else {
        if ( yDiff > 0 ) {
            /* up swipe */ 
        } else { 
            /* down swipe */
        }                                                                 
    }
    /* reset values */
    xDown = null;
    yDown = null;                                             
};

Probado en:

  • Android : Chrome, navegador UC
  • iOS : Safari, Chrome, navegador UC
nashcheez
fuente
originalEvent es una propiedad JQuery. Ni siquiera existe en Javascript puro.
Jan Derk
1
Según esta respuesta SO , se expondrá un evento táctil si es compatible con el navegador event.originalEvent. La cosa event.touchesha dejado de existir ahora y da como resultado undefined.
nashcheez
event.touches solo dejó de existir al usar JQuery. Pruebe su código sin JQuery y obtendrá un error que evt.originalEvent no está definido. JQuery reemplaza totalmente el evento por el suyo y coloca el evento del navegador nativo en el evento original. Versión corta: su código solo funciona con JQuery. Funciona sin JQuery si elimina originalevent.
Jan Derk
1
Sí, investigué un poco y me di cuenta de que tenías razón sobre la disponibilidad de jquery event.originalEvent. Actualizaré mi respuesta. ¡Gracias! :)
nashcheez
6

Algún mod de respuesta superior (no puedo comentar ...) para lidiar con golpes cortos

document.addEventListener('touchstart', handleTouchStart, false);        
document.addEventListener('touchmove', handleTouchMove, false);
var xDown = null;                                                        
var yDown = null;                                                        
function handleTouchStart(evt) {                                         
    xDown = evt.touches[0].clientX;                                      
    yDown = evt.touches[0].clientY;                                      
};                                                
function handleTouchMove(evt) {
    if ( ! xDown || ! yDown ) {
        return;
    }

    var xUp = evt.touches[0].clientX;                                    
    var yUp = evt.touches[0].clientY;

    var xDiff = xDown - xUp;
    var yDiff = yDown - yUp;
    if(Math.abs( xDiff )+Math.abs( yDiff )>150){ //to deal with to short swipes

    if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
        if ( xDiff > 0 ) {/* left swipe */ 
            alert('left!');
        } else {/* right swipe */
            alert('right!');
        }                       
    } else {
        if ( yDiff > 0 ) {/* up swipe */
            alert('Up!'); 
        } else { /* down swipe */
            alert('Down!');
        }                                                                 
    }
    /* reset values */
    xDown = null;
    yDown = null;
    }
};
rmnsh
fuente
6

trashold, timeipe swipe, swipeBlockElems add.

document.addEventListener('touchstart', handleTouchStart, false);
document.addEventListener('touchmove', handleTouchMove, false);
document.addEventListener('touchend', handleTouchEnd, false);     

const SWIPE_BLOCK_ELEMS = [
  'swipBlock',
  'handle',
  'drag-ruble'
]

let xDown = null;
let yDown = null; 
let xDiff = null;
let yDiff = null;
let timeDown = null;
const  TIME_TRASHOLD = 200;
const  DIFF_TRASHOLD = 130;

function handleTouchEnd() {

let timeDiff = Date.now() - timeDown; 
if (Math.abs(xDiff) > Math.abs(yDiff)) { /*most significant*/
  if (Math.abs(xDiff) > DIFF_TRASHOLD && timeDiff < TIME_TRASHOLD) {
    if (xDiff > 0) {
      // console.log(xDiff, TIME_TRASHOLD, DIFF_TRASHOLD)
      SWIPE_LEFT(LEFT) /* left swipe */
    } else {
      // console.log(xDiff)
      SWIPE_RIGHT(RIGHT) /* right swipe */
    }
  } else {
    console.log('swipeX trashhold')
  }
} else {
  if (Math.abs(yDiff) > DIFF_TRASHOLD && timeDiff < TIME_TRASHOLD) {
    if (yDiff > 0) {
      /* up swipe */
    } else {
      /* down swipe */
    }
  } else {
    console.log('swipeY trashhold')
  }
 }
 /* reset values */
 xDown = null;
 yDown = null;
 timeDown = null; 
}
function containsClassName (evntarget , classArr) {
 for (var i = classArr.length - 1; i >= 0; i--) {
   if( evntarget.classList.contains(classArr[i]) ) {
      return true;
    }
  }
}
function handleTouchStart(evt) {
  let touchStartTarget = evt.target;
  if( containsClassName(touchStartTarget, SWIPE_BLOCK_ELEMS) ) {
    return;
  }
  timeDown = Date.now()
  xDown = evt.touches[0].clientX;
  yDown = evt.touches[0].clientY;
  xDiff = 0;
  yDiff = 0;

}

function handleTouchMove(evt) {
  if (!xDown || !yDown) {
    return;
  }

  var xUp = evt.touches[0].clientX;
  var yUp = evt.touches[0].clientY;


  xDiff = xDown - xUp;
  yDiff = yDown - yUp;
}
Sergey Guns
fuente
4

Si alguien está tratando de usar jQuery Mobile en Android y tiene problemas con la detección de deslizamiento JQM

(Tenía algunos en Xperia Z1, Galaxy S3, Nexus 4 y algunos teléfonos Wiko también) esto puede ser útil:

 //Fix swipe gesture on android
    if(android){ //Your own device detection here
        $.event.special.swipe.verticalDistanceThreshold = 500
        $.event.special.swipe.horizontalDistanceThreshold = 10
    }

El deslizamiento en Android no se detectó a menos que fuera un deslizamiento muy largo, preciso y rápido.

Con estas dos líneas funciona correctamente.

Rayjax
fuente
También necesitaba agregar: ¡ $.event.special.swipe.scrollSupressionThreshold = 8;pero me pusiste en la dirección correcta! ¡Gracias!
Philip G
4

Tuve problemas con el controlador táctil disparando continuamente mientras el usuario arrastraba un dedo. No sé si eso se debe a algo que estoy haciendo mal o no, pero volví a cablear esto para acumular movimientos con touchmove y touchchend en realidad dispara la devolución de llamada.

También necesitaba tener una gran cantidad de estas instancias, así que agregué métodos de habilitación / deshabilitación.

Y un umbral donde un golpe corto no se dispara. Touchstart cero son los contadores cada vez.

Puede cambiar el target_node sobre la marcha. Habilitar en la creación es opcional.

/** Usage: */
touchevent = new Modules.TouchEventClass(callback, target_node);
touchevent.enable();
touchevent.disable();

/** 
*
*   Touch event module
*
*   @param method   set_target_mode
*   @param method   __touchstart
*   @param method   __touchmove
*   @param method   __touchend
*   @param method   enable
*   @param method   disable
*   @param function callback
*   @param node     target_node
*/
Modules.TouchEventClass = class {

    constructor(callback, target_node, enable=false) {

        /** callback function */
        this.callback = callback;

        this.xdown = null;
        this.ydown = null;
        this.enabled = false;
        this.target_node = null;

        /** move point counts [left, right, up, down] */
        this.counts = [];

        this.set_target_node(target_node);

        /** Enable on creation */
        if (enable === true) {
            this.enable();
        }

    }

    /** 
    *   Set or reset target node
    *
    *   @param string/node target_node
    *   @param string      enable (optional)
    */
    set_target_node(target_node, enable=false) {

        /** check if we're resetting target_node */
        if (this.target_node !== null) {

            /** remove old listener */
           this.disable();
        }

        /** Support string id of node */
        if (target_node.nodeName === undefined) {
            target_node = document.getElementById(target_node);
        }

        this.target_node = target_node;

        if (enable === true) {
            this.enable();
        }
    }

    /** enable listener */
    enable() {
        this.enabled = true;
        this.target_node.addEventListener("touchstart", this.__touchstart.bind(this));
        this.target_node.addEventListener("touchmove", this.__touchmove.bind(this));
        this.target_node.addEventListener("touchend", this.__touchend.bind(this));
    }

    /** disable listener */
    disable() {
        this.enabled = false;
        this.target_node.removeEventListener("touchstart", this.__touchstart);
        this.target_node.removeEventListener("touchmove", this.__touchmove);
        this.target_node.removeEventListener("touchend", this.__touchend);
    }

    /** Touchstart */
    __touchstart(event) {
        event.stopPropagation();
        this.xdown = event.touches[0].clientX;
        this.ydown = event.touches[0].clientY;

        /** reset count of moves in each direction, [left, right, up, down] */
        this.counts = [0, 0, 0, 0];
    }

    /** Touchend */
    __touchend(event) {
        let max_moves = Math.max(...this.counts);
        if (max_moves > 500) { // set this threshold appropriately
            /** swipe happened */
            let index = this.counts.indexOf(max_moves);
            if (index == 0) {
                this.callback("left");
            } else if (index == 1) {
                this.callback("right");
            } else if (index == 2) {
                this.callback("up");
            } else {
                this.callback("down");
            }
        }
    }

    /** Touchmove */
    __touchmove(event) {

        event.stopPropagation();
        if (! this.xdown || ! this.ydown) {
            return;
        }

        let xup = event.touches[0].clientX;
        let yup = event.touches[0].clientY;

        let xdiff = this.xdown - xup;
        let ydiff = this.ydown - yup;

        /** Check x or y has greater distance */
        if (Math.abs(xdiff) > Math.abs(ydiff)) {
            if (xdiff > 0) {
                this.counts[0] += Math.abs(xdiff);
            } else {
                this.counts[1] += Math.abs(xdiff);
            }
        } else {
            if (ydiff > 0) {
                this.counts[2] += Math.abs(ydiff);
            } else {
                this.counts[3] += Math.abs(ydiff);
            }
        }
    }
}
Punteras del trendal
fuente
¿Es esto para ES5 o ES6?
1.21 gigavatios
@gigawatts no recuerdo. El proyecto que usó ya llegó a EOL y no he necesitado el código desde entonces. Sospecho que cuando escribía para ES6, eso fue hace más de 2 años.
Trendal Toews
3

También fusioné algunas de las respuestas, principalmente la primera y la segunda con las clases, y aquí está mi versión:

export default class Swipe {
    constructor(options) {
        this.xDown = null;
        this.yDown = null;

        this.options = options;

        this.handleTouchStart = this.handleTouchStart.bind(this);
        this.handleTouchMove = this.handleTouchMove.bind(this);

        document.addEventListener('touchstart', this.handleTouchStart, false);
        document.addEventListener('touchmove', this.handleTouchMove, false);

    }

    onLeft() {
        this.options.onLeft();
    }

    onRight() {
        this.options.onRight();
    }

    onUp() {
        this.options.onUp();
    }

    onDown() {
        this.options.onDown();
    }

    static getTouches(evt) {
        return evt.touches      // browser API

    }

    handleTouchStart(evt) {
        const firstTouch = Swipe.getTouches(evt)[0];
        this.xDown = firstTouch.clientX;
        this.yDown = firstTouch.clientY;
    }

    handleTouchMove(evt) {
        if ( ! this.xDown || ! this.yDown ) {
            return;
        }

        let xUp = evt.touches[0].clientX;
        let yUp = evt.touches[0].clientY;

        let xDiff = this.xDown - xUp;
        let yDiff = this.yDown - yUp;


        if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
            if ( xDiff > 0 && this.options.onLeft) {
                /* left swipe */
                this.onLeft();
            } else if (this.options.onRight) {
                /* right swipe */
                this.onRight();
            }
        } else {
            if ( yDiff > 0 && this.options.onUp) {
                /* up swipe */
                this.onUp();
            } else if (this.options.onDown){
                /* down swipe */
                this.onDown();
            }
        }

        /* reset values */
        this.xDown = null;
        this.yDown = null;
    }
}

Luego puede usarlo de la siguiente manera:

let swiper = new Swipe({
                    onLeft() {
                        console.log('You swiped left.');
                    }
});

Ayuda a evitar errores de consola cuando desea llamar solo digamos el método "onLeft".

Romanna Semenyshyn
fuente
2

Si solo necesita deslizar, es mejor usar el tamaño que necesita. Esto debería funcionar en cualquier dispositivo táctil.

Esto es ~ 450 bytes 'después de la compresión gzip, minificación, babel, etc.

Escribí la clase a continuación en base a las otras respuestas, usa porcentaje movido en lugar de píxeles, y un patrón de despachador de eventos para enganchar / desenganchar cosas.

Úselo así:

const dispatcher = new SwipeEventDispatcher(myElement);
dispatcher.on('SWIPE_RIGHT', () => { console.log('I swiped right!') })

export class SwipeEventDispatcher {
	constructor(element, options = {}) {
		this.evtMap = {
			SWIPE_LEFT: [],
			SWIPE_UP: [],
			SWIPE_DOWN: [],
			SWIPE_RIGHT: []
		};

		this.xDown = null;
		this.yDown = null;
		this.element = element;
		this.options = Object.assign({ triggerPercent: 0.3 }, options);

		element.addEventListener('touchstart', evt => this.handleTouchStart(evt), false);
		element.addEventListener('touchend', evt => this.handleTouchEnd(evt), false);
	}

	on(evt, cb) {
		this.evtMap[evt].push(cb);
	}

	off(evt, lcb) {
		this.evtMap[evt] = this.evtMap[evt].filter(cb => cb !== lcb);
	}

	trigger(evt, data) {
		this.evtMap[evt].map(handler => handler(data));
	}

	handleTouchStart(evt) {
		this.xDown = evt.touches[0].clientX;
		this.yDown = evt.touches[0].clientY;
	}

	handleTouchEnd(evt) {
		const deltaX = evt.changedTouches[0].clientX - this.xDown;
		const deltaY = evt.changedTouches[0].clientY - this.yDown;
		const distMoved = Math.abs(Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY);
		const activePct = distMoved / this.element.offsetWidth;

		if (activePct > this.options.triggerPercent) {
			if (Math.abs(deltaX) > Math.abs(deltaY)) {
				deltaX < 0 ? this.trigger('SWIPE_LEFT') : this.trigger('SWIPE_RIGHT');
			} else {
				deltaY > 0 ? this.trigger('SWIPE_UP') : this.trigger('SWIPE_DOWN');
			}
		}
	}
}

export default SwipeEventDispatcher;

Vargr
fuente
2

Solo quería detectar el deslizamiento hacia la izquierda y la derecha, pero activar la acción solo cuando finaliza el evento táctil , por lo que modifiqué ligeramente la gran respuesta de @ givanse para hacerlo.

¿Por qué hacer eso? Si, por ejemplo, al deslizar, el usuario se da cuenta de que finalmente no quiere deslizar, puede mover el dedo en la posición original (una aplicación de teléfono "de citas" muy popular hace esto;)), y luego "deslizar hacia la derecha" El evento se cancela.

Entonces, para evitar un evento de "deslizar hacia la derecha" solo porque hay una diferencia de 3px horizontalmente, agregué un umbral por debajo del cual se descarta un evento: para tener un evento de "deslizar hacia la derecha", el usuario debe deslizar al menos 1/3 del ancho del navegador (por supuesto, puede modificar esto).

Todos estos pequeños detalles mejoran la experiencia del usuario. Aquí está el código (Vanilla JS):

var xDown = null, yDown = null, xUp = null, yUp = null;
document.addEventListener('touchstart', touchstart, false);        
document.addEventListener('touchmove', touchmove, false);
document.addEventListener('touchend', touchend, false);
function touchstart(evt) { const firstTouch = (evt.touches || evt.originalEvent.touches)[0]; xDown = firstTouch.clientX; yDown = firstTouch.clientY; }
function touchmove(evt) { if (!xDown || !yDown ) return; xUp = evt.touches[0].clientX; yUp = evt.touches[0].clientY; }
function touchend(evt) { 
    var xDiff = xUp - xDown, yDiff = yUp - yDown;
    if ((Math.abs(xDiff) > Math.abs(yDiff)) && (Math.abs(xDiff) > 0.33 * document.body.clientWidth)) { 
        if (xDiff < 0) 
            document.getElementById('leftnav').click();
        else
            document.getElementById('rightnav').click();
    } 
    xDown = null, yDown = null;
}
Basj
fuente
1

Ejemplo simple de vanilla JS para deslizar horizontalmente:

let touchstartX = 0
let touchendX = 0

const slider = document.getElementById('slider')

function handleGesure() {
  if (touchendX < touchstartX) alert('swiped left!')
  if (touchendX > touchstartX) alert('swiped right!')
}

slider.addEventListener('touchstart', e => {
  touchstartX = e.changedTouches[0].screenX
})

slider.addEventListener('touchend', e => {
  touchendX = e.changedTouches[0].screenX
  handleGesure()
})

Puede usar la misma lógica para deslizar verticalmente.

Damjan Pavlica
fuente
1

Agregando a esta respuesta aquí . Este agrega soporte para eventos de mouse para probar en el escritorio:

<!--scripts-->
class SwipeEventDispatcher {
    constructor(element, options = {}) {
        this.evtMap = {
            SWIPE_LEFT: [],
            SWIPE_UP: [],
            SWIPE_DOWN: [],
            SWIPE_RIGHT: []
        };

        this.xDown = null;
        this.yDown = null;
        this.element = element;
        this.isMouseDown = false;
        this.listenForMouseEvents = true;
        this.options = Object.assign({ triggerPercent: 0.3 }, options);

        element.addEventListener('touchstart', evt => this.handleTouchStart(evt), false);
        element.addEventListener('touchend', evt => this.handleTouchEnd(evt), false);
        element.addEventListener('mousedown', evt => this.handleMouseDown(evt), false);
        element.addEventListener('mouseup', evt => this.handleMouseUp(evt), false);
    }

    on(evt, cb) {
        this.evtMap[evt].push(cb);
    }

    off(evt, lcb) {
        this.evtMap[evt] = this.evtMap[evt].filter(cb => cb !== lcb);
    }

    trigger(evt, data) {
        this.evtMap[evt].map(handler => handler(data));
    }

    handleTouchStart(evt) {
        this.xDown = evt.touches[0].clientX;
        this.yDown = evt.touches[0].clientY;
    }

    handleMouseDown(evt) {
        if (this.listenForMouseEvents==false) return;
        this.xDown = evt.clientX;
        this.yDown = evt.clientY;
        this.isMouseDown = true;
    }

    handleMouseUp(evt) {
        if (this.isMouseDown == false) return;
        const deltaX = evt.clientX - this.xDown;
        const deltaY = evt.clientY - this.yDown;
        const distMoved = Math.abs(Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY);
        const activePct = distMoved / this.element.offsetWidth;

        if (activePct > this.options.triggerPercent) {
            if (Math.abs(deltaX) > Math.abs(deltaY)) {
                deltaX < 0 ? this.trigger('SWIPE_LEFT') : this.trigger('SWIPE_RIGHT');
            } else {
                deltaY > 0 ? this.trigger('SWIPE_UP') : this.trigger('SWIPE_DOWN');
            }
        }
    }

    handleTouchEnd(evt) {
        const deltaX = evt.changedTouches[0].clientX - this.xDown;
        const deltaY = evt.changedTouches[0].clientY - this.yDown;
        const distMoved = Math.abs(Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY);
        const activePct = distMoved / this.element.offsetWidth;

        if (activePct > this.options.triggerPercent) {
            if (Math.abs(deltaX) > Math.abs(deltaY)) {
                deltaX < 0 ? this.trigger('SWIPE_LEFT') : this.trigger('SWIPE_RIGHT');
            } else {
                deltaY > 0 ? this.trigger('SWIPE_UP') : this.trigger('SWIPE_DOWN');
            }
        }
    }
}

// add a listener on load
window.addEventListener("load", function(event) {
    const dispatcher = new SwipeEventDispatcher(document.body);
    dispatcher.on('SWIPE_RIGHT', () => { console.log('I swiped right!') })
    dispatcher.on('SWIPE_LEFT', () => { console.log('I swiped left!') })
});
1,21 gigavatios
fuente
1

I reelaborado @givanse solución 's de funcionar como un Reaccionar gancho. La entrada es algunos oyentes de eventos opcionales, la salida es una referencia funcional (debe ser funcional para que el gancho pueda volver a ejecutarse cuando / si la referencia cambia).

También se agrega en el parámetro de umbral de deslizamiento vertical / horizontal, para que pequeños movimientos no activen accidentalmente a los oyentes del evento, pero estos se pueden establecer en 0 para imitar la respuesta original más de cerca.

Consejo: para obtener el mejor rendimiento, las funciones de entrada del oyente de eventos deben ser memorizadas.

function useSwipeDetector({
    // Event listeners.
    onLeftSwipe,
    onRightSwipe,
    onUpSwipe,
    onDownSwipe,

    // Threshold to detect swipe.
    verticalSwipeThreshold = 50,
    horizontalSwipeThreshold = 30,
}) {
    const [domRef, setDomRef] = useState(null);
    const xDown = useRef(null);
    const yDown = useRef(null);

    useEffect(() => {
        if (!domRef) {
            return;
        }

        function handleTouchStart(evt) {
            const [firstTouch] = evt.touches;
            xDown.current = firstTouch.clientX;
            yDown.current = firstTouch.clientY;
        };

        function handleTouchMove(evt) {
            if (!xDown.current || !yDown.current) {
                return;
            }

            const [firstTouch] = evt.touches;
            const xUp = firstTouch.clientX;
            const yUp = firstTouch.clientY;
            const xDiff = xDown.current - xUp;
            const yDiff = yDown.current - yUp;

            if (Math.abs(xDiff) > Math.abs(yDiff)) {/*most significant*/
                if (xDiff > horizontalSwipeThreshold) {
                    if (onRightSwipe) onRightSwipe();
                } else if (xDiff < -horizontalSwipeThreshold) {
                    if (onLeftSwipe) onLeftSwipe();
                }
            } else {
                if (yDiff > verticalSwipeThreshold) {
                    if (onUpSwipe) onUpSwipe();
                } else if (yDiff < -verticalSwipeThreshold) {
                    if (onDownSwipe) onDownSwipe();
                }
            }
        };

        function handleTouchEnd() {
            xDown.current = null;
            yDown.current = null;
        }

        domRef.addEventListener("touchstart", handleTouchStart, false);
        domRef.addEventListener("touchmove", handleTouchMove, false);
        domRef.addEventListener("touchend", handleTouchEnd, false);

        return () => {
            domRef.removeEventListener("touchstart", handleTouchStart);
            domRef.removeEventListener("touchmove", handleTouchMove);
            domRef.removeEventListener("touchend", handleTouchEnd);
        };
    }, [domRef, onLeftSwipe, onRightSwipe, onUpSwipe, onDownSwipe, verticalSwipeThreshold, horizontalSwipeThreshold]);

    return (ref) => setDomRef(ref);
};
Ruben Martinez Jr.
fuente
0

Un ejemplo de cómo usar con desplazamiento.

// at least 100 px are a swipe
// you can use the value relative to screen size: window.innerWidth * .1
const offset = 100;
let xDown, yDown

window.addEventListener('touchstart', e => {
  const firstTouch = getTouch(e);

  xDown = firstTouch.clientX;
  yDown = firstTouch.clientY;
});

window.addEventListener('touchend', e => {
  if (!xDown || !yDown) {
    return;
  }

  const {
    clientX: xUp,
    clientY: yUp
  } = getTouch(e);
  const xDiff = xDown - xUp;
  const yDiff = yDown - yUp;
  const xDiffAbs = Math.abs(xDown - xUp);
  const yDiffAbs = Math.abs(yDown - yUp);

  // at least <offset> are a swipe
  if (Math.max(xDiffAbs, yDiffAbs) < offset ) {
    return;
  }

  if (xDiffAbs > yDiffAbs) {
    if ( xDiff > 0 ) {
      console.log('left');
    } else {
      console.log('right');
    }
  } else {
    if ( yDiff > 0 ) {
      console.log('up');
    } else {
      console.log('down');
    }
  }
});

function getTouch (e) {
  return e.changedTouches[0]
}

Илья Зеленько
fuente
Actualmente usando esta versión. ¿Cómo podría evitar que esto se dispare varias veces si se desliza repetidamente? Utilizo esto con la función animada para una forma de desplazamiento lateral y cuando deslizo varias veces, las cosas se vuelven un poco complicadas y mis divs comienzan a superponerse en el área visible.
NMALM
0

Puede que le resulte más fácil implementarlo primero con los eventos del mouse para crear un prototipo.

Aquí hay muchas respuestas, incluida la parte superior, debe usarse con precaución, ya que no consideran los casos límite, especialmente alrededor de los cuadros delimitadores.

Ver:

Deberá experimentar para detectar casos y comportamientos extremos, como el puntero que se mueve fuera del elemento antes de finalizar.

Un deslizamiento es un gesto muy básico que es un nivel más alto de procesamiento de interacción con el puntero de la interfaz, aproximadamente entre el procesamiento de eventos sin procesar y el reconocimiento de escritura a mano.

No existe un método exacto único para detectar un deslizamiento o un lanzamiento, aunque prácticamente todos generalmente siguen un principio básico de detectar un movimiento a través de un elemento con un umbral de distancia y velocidad o velocidad. Simplemente puede decir que si hay un movimiento en el 65% del tamaño de la pantalla en una dirección determinada dentro de un tiempo determinado, entonces es un deslizamiento. Exactamente dónde dibuja la línea y cómo la calcula depende de usted.

Algunos también pueden mirarlo desde la perspectiva del impulso en una dirección y qué tan lejos de la pantalla ha sido empujado cuando se suelta el elemento. Esto es más claro con deslizamientos adhesivos donde el elemento puede arrastrarse y luego al soltarse rebotará o saldrá volando de la pantalla como si el elástico se rompiera.

Probablemente sea ideal tratar de encontrar una biblioteca de gestos que pueda portar o reutilizar y que se use comúnmente para mantener la coherencia. Muchos de los ejemplos aquí son excesivamente simplistas, registrando un deslizamiento como el toque más leve en cualquier dirección.

Android sería la opción obvia, aunque tiene el problema opuesto, es demasiado complejo.

Muchas personas parecen haber malinterpretado la pregunta como cualquier movimiento en una dirección. Un deslizamiento es un movimiento amplio y relativamente breve abrumadoramente en una sola dirección (aunque puede ser arqueado y tener ciertas propiedades de aceleración). Un lanzamiento es similar, aunque tiene la intención de impulsar casualmente un elemento a una distancia considerable bajo su propio impulso.

Los dos son lo suficientemente similares como para que algunas bibliotecas solo proporcionen lanzamiento o deslizamiento, que se pueden usar indistintamente. En una pantalla plana, es difícil separar realmente los dos gestos y, en general, las personas están haciendo ambas cosas (deslizando la pantalla física pero arrojando el elemento UI que se muestra en la pantalla).

Tu mejor opción es no hacerlo tú mismo. Ya hay una gran cantidad de bibliotecas JavaScript para detectar gestos simples .

jgmjgm
fuente