Erkennen Sie einen Fingerabdruck durch JavaScript auf dem iPhone und Android

268

Wie können Sie feststellen, dass ein Benutzer seinen Finger mit JavaScript in eine Richtung über eine Webseite bewegt hat?

Ich habe mich gefragt, ob es eine Lösung gibt, die für Websites sowohl auf dem iPhone als auch auf einem Android-Telefon funktioniert.

827
quelle
1
Für die Swipe-Erkennung würde ich Hammer.js empfehlen . Es ist ziemlich klein und unterstützt viele Gesten: - Wischen - Drehen - Drücken -
Will Brickner
Es gibt ein Ereignis: "touchmove"
Clay
@ Ton, dass man in Safari immer noch nicht funktioniert, also kein iPhone.
Jakuje

Antworten:

341

Einfaches Vanilla JS-Codebeispiel:

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

In Android getestet.

givanse
quelle
1
Sieht cool und einfach aus. Irgendeine Idee, was die Unterstützung für diese Veranstaltungen touchstartist touchmove?
d.raev
1
Es funktioniert ziemlich gut, hat aber ein Problem beim Erkennen von geraden Bewegungen. Ich werde zu diesem Thema eine weitere Antwort veröffentlichen, die dies als JQuery (Desktop) -Lösung behoben hat. Außerdem wird die Mausversion dieser Wischereignisse hinzugefügt und eine Empfindlichkeitsoption hinzugefügt.
Codebeat
1
Verdammt. Das Thema ist geschlossen, daher kann ich meine Antwort nicht hinzufügen!
Codebeat
3
Das funktioniert gut, aber links / rechts und oben / unten sind rückwärts.
Peter Eisentraut
4
originalEvent ist eine JQuery-Eigenschaft. Es sollte weggelassen werden, wenn Sie reines Javascript ohne JQuery ausführen. Der aktuelle Code löst eine Ausnahme aus, wenn er ohne JQuery ausgeführt wird.
Jan Derk
31

Basierend auf der Antwort von @ givanse können Sie dies folgendermaßen tun 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);
    }
}

Sie können es dann so verwenden:

// 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
quelle
7
Dieser Code funktioniert wahrscheinlich nicht, weil Sie beim Versuch, .bindundefined aufzurufen, eine Ausnahme erhalten, weil Sie handleTouchMovetatsächlich nichts zurückgegeben haben. Außerdem ist es sinnlos, bind aufzurufen, wenn eine Funktion mit aufgerufen wird, this.da sie bereits an den aktuellen Kontext gebunden ist
nick.skriabin
3
Ich habe es gerade entfernt .bind(this);und es hat anmutig funktioniert. danke @nicholas_r
Ali Ghanavatian
Teil das Element selbst holen Ich entferne einfach '#' in document.getElementById ('mein-Element') und es hat gut funktioniert. Vielen Dank, dass Sie @Marwelln :)
Blue Tram
4
Wenn Sie warten möchten, bis das Wischen ENDET (dh nachdem sie ihren Finger oder ihre Maus angehoben haben), wechseln Sie touches[0]zu changedTouches[0]und der Ereignishandlertyp handleTouchMovezuhandleTouchEnd
TetraDev
Rufen Sie run()zweimal an und Sie bekommen ein böses Speicherleck
Mat
20

Ich habe einige der Antworten hier in einem Skript zusammengeführt, das CustomEvent verwendet , um geklaute Ereignisse im DOM auszulösen. Fügen Sie Ihrer Seite das Skript 0,7k swiped- events.min.js hinzu und warten Sie auf geklaute Ereignisse:

nach links gewischt

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

nach rechts gewischt

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

gewischt

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

runtergewischt

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

Sie können auch direkt an ein Element anhängen:

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

Optionale Konfiguration

Sie können die folgenden Attribute angeben, um die Funktionsweise der Swipe-Interaktion auf Ihrer Seite zu optimieren (diese sind optional) .

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

Der Quellcode ist auf Github verfügbar

John Doherty
quelle
Ich bin hierher gekommen, weil Pure-Swipe bei MOBILE
StefanBob
@StefanBob, wenn Sie ein Häkchen auf dem Github-Repo mit genügend Informationen setzen, damit ich das Problem reproduzieren kann, werde ich es untersuchen
John Doherty
1
Danke, es funktioniert perfekt! Ich habe Hammer.js durch Ihre Bibliothek ersetzt, da erstere nicht mit dem Browser-Zoom funktioniert und dies ein ernstes Usability-Problem darstellt. Mit dieser Bibliothek funktioniert der Zoom ordnungsgemäß (auf Android getestet)
Collimarco
15

Was ich zuvor verwendet habe, ist, dass Sie das Mousedown-Ereignis erkennen, seine x-, y-Position (je nachdem, was relevant ist) aufzeichnen, dann das Mouseup-Ereignis erkennen und die beiden Werte subtrahieren müssen.

halloandre
quelle
28
Ich glaube, es ist Touchstart, Touchmove, Touchcancel und Touchend, mit denen man arbeiten würde, nicht Mousedown oder Mouseup.
Volomike
15

jQuery Mobile bietet auch Swipe-Unterstützung: http://api.jquerymobile.com/swipe/

Beispiel

$("#divId").on("swipe", function(event) {
    alert("It's a swipe!");
});
Grinn
quelle
4
Wenn Sie nicht möchten, dass jQuery Mobile die Benutzeroberfläche manipuliert, lesen
Niklas Ekman
12

Ich habe festgestellt, dass die brillante Antwort von @givanse für mehrere mobile Browser die zuverlässigste und kompatibelste ist, um Swipe-Aktionen zu registrieren.

Es ist jedoch eine Änderung in seinem Code erforderlich, damit er in modernen mobilen Browsern funktioniert, die ihn verwenden jQuery.

event.touchesexistiert nicht, wenn jQueryes verwendet wird und führt undefinedund ersetzt werden durch event.originalEvent.touches. Ohne jQuery, event.touchessollte gut funktionieren.

So wird die Lösung,

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

Getestet am:

  • Android : Chrome, UC-Browser
  • iOS : Safari, Chrome, UC Browser
nashcheez
quelle
originalEvent ist eine JQuery-Eigenschaft. Es existiert nicht einmal in reinem Javascript.
Jan Derk
1
Gemäß dieser SO-Antwort wird ein Berührungsereignis angezeigt, wenn es vom Browser unterstützt wird event.originalEvent. Das Ding event.toucheshat aufgehört zu existieren und führt dazu undefined.
Nashcheez
event.touches existierte nur bei Verwendung von JQuery nicht mehr. Versuchen Sie Ihren Code ohne JQuery und Sie erhalten eine Fehlermeldung, dass evt.originalEvent undefiniert ist. JQuery ersetzt das Ereignis vollständig durch ein eigenes und versetzt das native Browserereignis in originalevent. Kurzversion: Ihr Code funktioniert nur mit JQuery. Es funktioniert ohne JQuery, wenn Sie originalevent entfernen.
Jan Derk
1
Ja, ich habe ein bisschen recherchiert und festgestellt, dass Sie mit der Verfügbarkeit von JQuery-Funktionen Recht hatten event.originalEvent . Ich werde meine Antwort aktualisieren. Vielen Dank! :)
Nashcheez
6

Einige Mods der besten Antwort (kann nicht kommentieren ...), um mit kurzen Wischbewegungen umzugehen

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
quelle
6

trashold, timeout swipe, swipeBlockElems hinzufügen.

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
quelle
4

Wenn jemand versucht, jQuery Mobile unter Android zu verwenden, und Probleme mit der JQM-Swipe-Erkennung hat

(Ich hatte einige auf Xperia Z1, Galaxy S3, Nexus 4 und einigen Wiko-Handys) Dies kann nützlich sein:

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

Swipe on Android wurde nicht erkannt, es sei denn, es war ein sehr langer, präziser und schneller Swipe.

Mit diesen beiden Zeilen funktioniert es richtig

Rayjax
quelle
Ich musste auch hinzufügen: $.event.special.swipe.scrollSupressionThreshold = 8;aber du hast mich in die richtige Richtung gebracht! Danke dir!
Philip G
4

Ich hatte Probleme damit, dass der Touchend-Handler kontinuierlich feuerte, während der Benutzer einen Finger herumzog. Ich weiß nicht, ob das an etwas liegt, was ich falsch mache oder nicht, aber ich habe dies neu verkabelt, um Bewegungen mit touchmove zu akkumulieren, und touchend löst tatsächlich den Rückruf aus.

Ich brauchte auch eine große Anzahl dieser Instanzen und fügte daher Aktivierungs- / Deaktivierungsmethoden hinzu.

Und eine Schwelle, bei der ein kurzer Schlag nicht ausgelöst wird. Touchstart Null ist jedes Mal die Zähler.

Sie können den target_node im laufenden Betrieb ändern. Bei Erstellung aktivieren ist optional.

/** 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);
            }
        }
    }
}
Trendal Toews
quelle
Ist das für ES5 oder ES6?
1,21 Gigawatt
@ Gigawatt Ich erinnere mich nicht. Das verwendete Projekt hat EOL bereits erreicht und ich habe den Code seitdem nicht mehr benötigt. Ich vermute zu der Zeit, als ich für ES6 schrieb, aber das war vor über 2 Jahren.
Trendal Toews
3

Ich habe auch einige der Antworten zusammengeführt, hauptsächlich die erste und die zweite mit Klassen, und hier ist meine Version:

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

Danach kann es wie folgt verwendet werden:

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

Es hilft, Konsolenfehler zu vermeiden, wenn Sie nur die Methode "onLeft" aufrufen möchten.

Romanna Semenyshyn
quelle
2

Wenn Sie nur wischen müssen, ist es besser, wenn Sie nur das Teil verwenden, das Sie benötigen. Dies sollte auf jedem Touch-Gerät funktionieren.

Dies sind ~ 450 Bytes nach gzip-Komprimierung, Minimierung, Babel usw.

Ich habe die folgende Klasse basierend auf den anderen Antworten geschrieben. Sie verwendet den Prozentsatz der verschobenen Pixel anstelle von Pixeln und ein Ereignis-Dispatcher-Muster, um Dinge zu verknüpfen / zu lösen.

Verwenden Sie es so:

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
quelle
2

Ich wollte nur das Wischen nach links und rechts erkennen, aber die Aktion erst auslösen, wenn das Berührungsereignis endet. Deshalb habe ich die großartige Antwort von @ givanse leicht geändert, um dies zu tun.

Warum das tun? Wenn der Benutzer beispielsweise beim Wischen bemerkt, dass er endlich nicht wischen möchte, kann er seinen Finger an die ursprüngliche Position bewegen (eine sehr beliebte "Dating" -Telefonanwendung tut dies;)) und dann das "Wischen nach rechts". Veranstaltung wird abgesagt.

Um ein "nach rechts wischen" -Ereignis zu vermeiden, nur weil es horizontal einen Unterschied von 3 Pixel gibt, habe ich einen Schwellenwert hinzugefügt, unter dem ein Ereignis verworfen wird: Um ein "nach rechts wischen" -Ereignis zu haben, muss der Benutzer mindestens wischen 1/3 der Browserbreite (natürlich können Sie dies ändern).

All diese kleinen Details verbessern die Benutzererfahrung. Hier ist der (Vanilla JS) Code:

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
quelle
1

Einfaches Vanille-JS-Beispiel für horizontales Wischen:

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

Sie können für vertikales Wischen ziemlich dieselbe Logik verwenden.

Damjan Pavlica
quelle
1

Zu dieser Antwort hier hinzufügen . Dieser bietet Unterstützung für Mausereignisse zum Testen auf dem 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,21 Gigawatt
quelle
0

Ein Beispiel für die Verwendung mit 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]
}

Илья Зеленько
quelle
Derzeit wird diese Version verwendet. Wie würde ich verhindern, dass dies mehrmals ausgelöst wird, wenn ich wiederholt wische? Ich benutze dies mit der Animationsfunktion für ein Side-Scrolling-Formular. Wenn ich mehrmals wische, werden die Dinge etwas verrückt und meine Divs beginnen sich im sichtbaren Bereich zu überlappen.
NMALM
0

Möglicherweise fällt es Ihnen leichter, es zuerst mit Mausereignissen als Prototyp zu implementieren.

Es gibt hier viele Antworten, einschließlich der oberen, die mit Vorsicht verwendet werden sollten, da sie Randfälle nicht berücksichtigen, insbesondere in der Nähe von Begrenzungsrahmen.

Sehen:

Sie müssen experimentieren, um Kantenfälle und Verhaltensweisen zu erfassen, z. B. den Zeiger, der sich vor dem Ende außerhalb des Elements bewegt.

Ein Wischen ist eine sehr grundlegende Geste, bei der es sich um eine höhere Ebene der Interaktionsverarbeitung von Schnittstellenzeigern handelt, die ungefähr zwischen der Verarbeitung von Rohereignissen und der Handschrifterkennung liegt.

Es gibt keine exakte Methode zum Erkennen eines Schlagens oder Schleuderns, obwohl praktisch alle im Allgemeinen einem Grundprinzip des Erkennens einer Bewegung über ein Element mit einem Schwellenwert für Entfernung, Geschwindigkeit oder Geschwindigkeit folgen. Man könnte einfach sagen, wenn sich innerhalb einer bestimmten Zeit eine Bewegung über 65% der Bildschirmgröße in eine bestimmte Richtung bewegt, handelt es sich um einen Wisch. Es liegt an Ihnen, wo Sie die Linie ziehen und wie Sie sie berechnen.

Einige betrachten es möglicherweise auch aus der Perspektive des Impulses in eine Richtung und wie weit es vom Bildschirm entfernt ist, wenn das Element losgelassen wird. Dies ist klarer bei klebrigen Wischbewegungen, bei denen das Element gezogen werden kann und dann beim Loslassen entweder zurückprallt oder vom Bildschirm fliegt, als ob das Gummiband gebrochen wäre.

Es ist wahrscheinlich ideal, um zu versuchen, eine Gestenbibliothek zu finden, die Sie entweder portieren oder wiederverwenden können und die üblicherweise aus Gründen der Konsistenz verwendet wird. Viele der Beispiele hier sind zu simpel und registrieren einen Schlag als die geringste Berührung in eine beliebige Richtung.

Android wäre die offensichtliche Wahl, hat aber das gegenteilige Problem, es ist zu komplex.

Viele Menschen scheinen die Frage als eine Bewegung in eine Richtung falsch interpretiert zu haben. Ein Schlag ist eine breite und relativ kurze Bewegung, die überwiegend in eine Richtung geht (obwohl sie bogenförmig sein kann und bestimmte Beschleunigungseigenschaften aufweist). Ein Fling ist ähnlich, beabsichtigt jedoch, einen Gegenstand aus eigener Kraft beiläufig ein gutes Stück wegzutreiben.

Die beiden sind sich so ähnlich, dass einige Bibliotheken möglicherweise nur Fling oder Swipe bereitstellen, die austauschbar verwendet werden können. Auf einem Flachbildschirm ist es schwierig, die beiden Gesten wirklich zu trennen, und im Allgemeinen tun die Leute beides (Wischen des physischen Bildschirms, aber Schleudern des auf dem Bildschirm angezeigten UI-Elements).

Sie können es am besten nicht selbst tun. Es gibt bereits eine große Anzahl von JavaScript-Bibliotheken zum Erkennen einfacher Gesten .

jgmjgm
quelle
0

Ich habe die Lösung von @givanse überarbeitet von um als React-Hook zu fungieren. Die Eingabe ist ein optionaler Ereignis-Listener. Die Ausgabe ist eine funktionale Referenz (muss funktionsfähig sein, damit der Hook erneut ausgeführt werden kann, wenn sich die Referenz ändert).

Wird auch im vertikalen / horizontalen Swipe-Schwellenwertparameter hinzugefügt, damit kleine Bewegungen die Ereignis-Listener nicht versehentlich auslösen. Diese können jedoch auf 0 gesetzt werden, um die ursprüngliche Antwort genauer nachzuahmen.

Tipp: Für eine optimale Leistung sollten die Eingabefunktionen des Ereignis-Listeners gespeichert werden.

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.
quelle