Rileva un passaggio del dito attraverso JavaScript su iPhone e Android


268

Come è possibile rilevare che un utente ha fatto scorrere il dito in una direzione su una pagina Web con JavaScript?

Mi chiedevo se esistesse una soluzione che funzionasse per i siti Web sia su iPhone che su un telefono Android.


1
Per il riconoscimento del colpo, consiglierei Hammer.js . È piuttosto piccolo e supporta molti gesti: - Scorri - Ruota - Pizzica - Premi (tieni premuto a lungo) - Tocca - Pan
Will Brickner

C'è un evento: "touchmove"
Clay

@Clay che uno ancora non funziona in Safari, quindi nessun iPhone.
Jakuje,

Risposte:


342

Esempio di codice JS semplice alla vaniglia:

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;                                             
};

Testato su Android.


1
Sembra freddo e semplice, qualsiasi idea di che cosa è il supporto per questi eventi touchstart, touchmove?
d.raev,

1
Funziona abbastanza bene ma ha un problema nel rilevare movimenti dritti. Invierò un'altra risposta a questo argomento che ha risolto questo problema come soluzione JQuery (desktop). Aggiunge inoltre la versione del mouse di questi eventi di scorrimento e aggiunge un'opzione di sensibilità.
Codebeat

1
Dannazione. L'argomento è chiuso, quindi non posso aggiungere la mia risposta!
Codebeat

3
Funziona benissimo, ma sinistra / destra e su / giù sono all'indietro.
Peter Eisentraut,

4
originalEvent è una proprietà JQuery. Dovrebbe essere lasciato fuori se si esegue javascript puro senza JQuery. Il codice corrente genera un'eccezione se eseguito senza JQuery.
Jan Derk,

31

Basato sulla risposta di @ givanse, ecco come potresti farlo 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);
    }
}

Puoi usarlo così:

// 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();

7
questo codice probabilmente non funzionerà perché otterrai un'eccezione mentre provi a chiamare .bindindefinito perché in handleTouchMoverealtà non hai restituito nulla. inoltre è inutile chiamare bind quando si chiama la funzione this.perché è già legato al contesto corrente
nick.skriabin

3
Ho appena rimosso .bind(this);e ha funzionato con grazia. grazie @nicholas_r
Ali Ghanavatian

In parte ottieni l'elemento da solo Ho appena rimosso '#' in document.getElementById ('my-element') e ha funzionato bene. Grazie @Marwelln :)
Blue Tram

4
Se si desidera attendere fino allo scadere del dito (ovvero dopo che hanno alzato il dito o acceso), passare touches[0]a changedTouches[0]e il tipo di gestore eventi handleTouchMovesuhandleTouchEnd
TetraDev

chiama run()due volte e hai una brutta perdita di memoria
Mat

20

Ho unito alcune delle risposte qui in uno script che utilizza CustomEvent per generare eventi trascinati nel DOM. Aggiungi lo script 0.7k swiped-events.min.js alla tua pagina e ascolta gli eventi passati :

strisciato-sinistra

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

strisciato-destra

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

strisciato-up

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

strisciato-down

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

Puoi anche collegarti direttamente a un elemento:

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

Configurazione opzionale

Puoi specificare i seguenti attributi per modificare il funzionamento dell'interazione con il dito nella tua pagina (questi sono opzionali) .

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

Il codice sorgente è disponibile su Github


Sono venuto qui perché il colpo puro non funzionava per me su MOBILE
StefanBob

@StefanBob se alzi un segno di spunta sul repository github con informazioni sufficienti per consentirmi di riprodurre il problema, lo esaminerò
John Doherty,

1
Grazie, funziona perfettamente! Ho sostituito Hammer.js con la tua libreria, perché il primo non funziona con lo zoom del browser e questo è un serio problema di usabilità. Con questa libreria lo zoom funziona correttamente (testato su Android)
collimarco,

15

quello che ho usato prima è che devi rilevare l'evento mousedown, registrare la sua posizione x, y (qualunque sia rilevante), quindi rilevare l'evento mouseup e sottrarre i due valori.


28
Credo che sia touchstart, touchmove, touchcancel e touchend con cui si dovrebbe lavorare, non tra mouse e mouse.
Volomike,


12

Ho trovato la brillante risposta di @givanse come la più affidabile e compatibile su più browser mobili per la registrazione delle azioni di scorrimento.

Tuttavia, c'è un cambiamento nel suo codice richiesto per farlo funzionare nei browser mobili moderni che stanno utilizzando jQuery.

event.touchesnon esiste se jQueryviene utilizzato e risulta undefinede deve essere sostituito da event.originalEvent.touches. Senza jQuery, event.touchesdovrebbe funzionare bene.

Quindi la soluzione diventa,

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;                                             
};

Testato su:

  • Android : Chrome, browser UC
  • iOS : Safari, Chrome, UC Browser

originalEvent è una proprietà JQuery. Non esiste nemmeno in Javascript puro.
Jan Derk,

1
Come da questa risposta SO , verrà visualizzato un evento touch se supportato dal browser event.originalEvent. La cosa è event.touchescessata di esistere ora e risulta undefined.
nashcheez,

event.touches ha smesso di esistere solo quando si utilizza JQuery. Prova il tuo codice senza JQuery e otterrai un errore che evt.originalEvent non è definito. JQuery sostituisce totalmente l'evento con il proprio e mette l'evento del browser nativo in originalevent. Versione breve: il tuo codice funziona solo con JQuery. Funziona senza JQuery se rimuovi originalevent.
Jan Derk,

1
Sì, ho studiato un po 'e ho capito che avevi ragione sulla disponibilità dell'abilitazione jquery event.originalEvent. Aggiornerò la mia risposta. Grazie! :)
nashcheez,


6

Qualche mod di risposta più alta (non posso commentare ...) per gestire brevi passaggi

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;
    }
};

6

trashold, timeout 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;
}

4

Se qualcuno sta cercando di utilizzare jQuery Mobile su Android e ha problemi con il rilevamento dello scorrimento di JQM

(Ne ho avuti alcuni su Xperia Z1, Galaxy S3, Nexus 4 e anche alcuni telefoni Wiko) questo può essere utile:

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

Lo scorrimento su Android non è stato rilevato a meno che non fosse un passaggio molto lungo, preciso e veloce.

Con queste due linee funziona correttamente


Avevo anche bisogno di aggiungere: $.event.special.swipe.scrollSupressionThreshold = 8;ma mi hai messo nella giusta direzione! Grazie!
Filippo G,

4

Ho avuto problemi con l'handler touchend sparando continuamente mentre l'utente trascinava un dito. Non so se ciò sia dovuto a qualcosa che sto facendo di sbagliato o no, ma l'ho ricablato per accumulare mosse con touchmove e il touchend in realtà attiva il callback.

Avevo anche bisogno di avere un gran numero di queste istanze e quindi ho aggiunto i metodi di abilitazione / disabilitazione.

E una soglia in cui un colpo corto non si attiva. Touchstart zero è ogni volta i contatori.

Puoi cambiare il target_node al volo. Abilita alla creazione è facoltativo.

/** 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);
            }
        }
    }
}

È per ES5 o ES6?
1,21 gigawatt

@gigawatts Non ricordo. Il progetto che ha utilizzato che ha già raggiunto EOL e da allora non ho più avuto bisogno del codice. Ho il sospetto che stavo scrivendo per ES6, ma è stato più di 2 anni fa.
Trendal Toews

3

Ho unito anche alcune delle risposte, principalmente la prima e la seconda con le classi, ed ecco la mia versione:

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;
    }
}

Successivamente puoi usarlo come segue:

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

Aiuta a evitare errori di console quando si desidera chiamare solo diciamo il metodo "onLeft".



2

Se hai solo bisogno di scorrere, stai meglio con le dimensioni usando solo la parte che ti serve. Questo dovrebbe funzionare su qualsiasi dispositivo touch.

Questo è ~ 450 byte dopo compressione gzip, minificazione, babele ecc.

Ho scritto la classe seguente in base alle altre risposte, utilizza la percentuale spostata anziché i pixel e un modello di dispatcher di eventi per agganciare / sganciare le cose.

Usalo così:

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;


2

Volevo rilevare solo lo scorrimento verso destra e sinistra, ma attivare l'azione solo quando termina l' evento touch , quindi ho modificato leggermente la grande risposta di @ givanse per farlo.

Perché farlo? Se, ad esempio, durante lo scorrimento, l'utente nota che alla fine non vuole scorrere, può spostare il dito nella posizione originale (un'applicazione per telefono "appuntamenti" molto popolare fa questo;)), quindi "scorri verso destra" l'evento è cancellato.

Quindi per evitare un evento "scorri verso destra" solo perché c'è una differenza di 3px in orizzontale, ho aggiunto una soglia sotto la quale un evento viene scartato: per avere un evento "scorri verso destra", l'utente deve scorrere almeno 1/3 della larghezza del browser (ovviamente puoi modificarlo).

Tutti questi piccoli dettagli migliorano l'esperienza dell'utente. Ecco il codice (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;
}

1

Semplice esempio JS vaniglia per scorrimento orizzontale:

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()
})

Puoi usare la stessa logica per lo scorrimento verticale.


1

Aggiungendo a questa risposta qui . Questo aggiunge il supporto per gli eventi del mouse per i test sul desktop:

<!--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

Ho rielaborato la soluzione di @givanse per funzionare come un gancio React. L'input è un listener di eventi opzionale, l'output è un ref funzionale (deve essere funzionale in modo che l'hook possa essere eseguito nuovamente quando / se il ref cambia).

Aggiunto anche nel parametro soglia di scorrimento verticale / orizzontale, in modo che piccoli movimenti non attivino accidentalmente i listener di eventi, ma questi possono essere impostati su 0 per imitare più da vicino la risposta originale.

Suggerimento: per prestazioni ottimali, le funzioni di input del listener di eventi devono essere memorizzate.

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);
};

0

Un esempio di come utilizzare con offset.

// 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]
}


Attualmente in uso questa versione. Come eviterei che questo si spari più volte se scorressi ripetutamente? Lo utilizzo con la funzione animata per un modulo di registrazione laterale e quando scorro più volte, le cose diventano un po 'complicate e i miei div iniziano a sovrapporsi nell'area visibile.
NMALM,

0

Potrebbe essere più semplice implementarlo prima con gli eventi del mouse da prototipare.

Ci sono molte risposte qui, inclusa la parte superiore, che dovrebbero essere usate con cautela in quanto non considerano i casi limite soprattutto intorno ai riquadri.

Vedere:

Dovrai sperimentare per individuare casi e comportamenti come il puntatore che si sposta all'esterno dell'elemento prima di terminare.

Un colpo è un gesto molto semplice che è un livello più elevato di elaborazione dell'interazione del puntatore dell'interfaccia che si trova all'incirca tra l'elaborazione degli eventi non elaborati e il riconoscimento della grafia.

Non esiste un solo metodo esatto per rilevare un colpo o un'avventura sebbene praticamente tutti generalmente seguano un principio di base per rilevare un movimento attraverso un elemento con una soglia di distanza e velocità o velocità. Potresti semplicemente dire che se c'è un movimento attraverso il 65% della dimensione dello schermo in una data direzione entro un dato tempo, allora è un colpo. Esattamente dove disegni la linea e come la calcoli dipende da te.

Qualcuno potrebbe anche guardarlo dalla prospettiva della quantità di moto in una direzione e da quanto è stato spinto fuori dallo schermo quando l'elemento è stato rilasciato. Questo è più chiaro con i colpi appiccicosi in cui l'elemento può essere trascinato e poi al rilascio rimbalzerà indietro o volerà via dallo schermo come se l'elastico si spezzasse.

Probabilmente è l'ideale per provare a trovare una libreria di gesti che puoi portare o riutilizzare comunemente usata per coerenza. Molti degli esempi qui sono eccessivamente semplicistici, registrando un colpo come il minimo tocco in qualsiasi direzione.

Android sarebbe la scelta ovvia sebbene abbia il problema opposto, è eccessivamente complesso.

Molte persone sembrano aver frainteso la domanda come qualsiasi movimento in una direzione. Un colpo è un movimento ampio e relativamente breve in modo schiacciante in una sola direzione (sebbene possa essere arcuato e avere determinate proprietà di accelerazione). Un'avventura è simile sebbene intenda spingere casualmente un oggetto a una discreta distanza sotto il suo stesso slancio.

I due sono sufficientemente simili che alcune librerie potrebbero fornire solo lanciare o scorrere, che possono essere utilizzate in modo intercambiabile. Su uno schermo piatto è difficile separare veramente i due gesti e in generale le persone stanno facendo entrambe le cose (scorrendo lo schermo fisico ma lanciando l'elemento dell'interfaccia utente visualizzato sullo schermo).

L'opzione migliore è non farlo da soli. Esistono già molte librerie JavaScript per il rilevamento di gesti semplici .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.