Wie können Sie feststellen, dass ein Benutzer mit JavaScript über eine Webseite mit dem Finger in eine bestimmte Richtung gezogen hat?
Ich habe mich gefragt, ob es eine Lösung gibt, die sowohl für Websites auf dem iPhone als auch für ein Android-Handy geeignet ist.
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.
Ich fand dieses jquery touchwipe-Plugin, das sowohl auf meinem iPod touch der ersten Generation als auch auf meinem Droid funktioniert, unglaublich. http://www.netcu.de/jquery-touchwipe-iphone-ipad-library
Basierend auf der Antwort von @ givanse können Sie dies mit classes
tun:
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();
Hast du hammer.js ausprobiert? http://eightmedia.github.com/hammer.js/ Funktioniert auch auf Windows Phones ..
was ich bisher verwendet habe, ist das Erkennen des Mausedown-Ereignisses, das Aufzeichnen seiner x- und y-Position (je nachdem, was relevant ist), das Mouseup-Ereignis zu erkennen und die beiden Werte abzuziehen.
jQuery Mobile bietet auch Unterstützung für Swipe: http://api.jquerymobile.com/swipe/
Beispiel
$("#divId").on("swipe", function(event) {
alert("It's a swipe!");
});
Ich habe @givanse als brillante Antwort für mehrere mobile Browser als die zuverlässigste und kompatibelste Lösung für das Registrieren von Wischaktionen erwiesen.
Es ist jedoch eine Änderung in seinem Code erforderlich, damit er in modernen mobilen Browsern, die jQuery
verwenden, funktioniert.
event.touches
wird nicht vorhanden, wenn jQuery
verwendet wird und undefined
ergibt und durch event.originalEvent.touches
ersetzt werden sollte. Ohne jQuery
sollte event.touches
problemlos funktionieren.
Die Lösung wird also
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:
Ich habe TouchWipe
als kurzes Jquery-Plugin neu gepackt: detectSwipe
Ich habe ein paar Antworten hier in einem Skript zusammengeführt, das CustomEvent verwendet, um ausgelöste Ereignisse im DOM auszulösen. Fügen Sie Ihrer Seite das 0.7k pure-swipe.min.js -Skript hinzu und warten Sie auf swiped - Ereignisse:
document.addEventListener('swiped-left', function(e) {
console.log(e.target); // the element that was swiped
});
document.addEventListener('swiped-right', function(e) {
console.log(e.target); // the element that was swiped
});
document.addEventListener('swiped-up', function(e) {
console.log(e.target); // the element that was swiped
});
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
});
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>
Quellcode ist verfügbar auf Github
schwelle, Timeout Swipe, Swipe BlockElems 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;
}
Einige Mod der obersten Antwort (kann nicht kommentieren ...), die mit kurzen Wischereien zu tun hat
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;
}
};
Wenn jemand versucht, jQuery Mobile unter Android zu verwenden, und Probleme mit der JQM-Swipe-Erkennung auftreten
(Ich hatte einige für Xperia Z1, Galaxy S3, Nexus 4 und einige 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 auf Android wurde nicht erkannt, es sei denn, es war ein sehr langer, präziser und schneller Swipe.
Mit diesen beiden Zeilen funktioniert es korrekt
Ich hatte Probleme mit dem Touchend-Handler, der kontinuierlich feuert, während der Benutzer einen Finger herumzieht. Ich weiß nicht, ob das auf etwas zurückzuführen ist, das ich falsch mache oder nicht, aber ich habe das neu verkabelt, um mit touchmove Bewegungen anzusammeln, und das Touchend löst eigentlich den Rückruf aus.
Ich musste auch eine große Anzahl dieser Instanzen haben und fügte Aktivierungs-/Deaktivierungsmethoden hinzu.
Und eine Schwelle, bei der ein kurzer Schlag nicht ausgelöst wird. Touchstart setzt die Zähler jedes Mal auf Null.
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);
}
}
}
}
Verwendet zwei:
jQuery mobile: funktionieren in den meisten Fällen und besonders dann, wenn Sie eine Anwendung entwickeln, die ein anderes jQuery-Plugin verwendet. Besuchen Sie es hier: https://www.w3schools.com/jquerymobile/jquerymobile_events_touch.asp
Hammerzeit! eine der besten, leichtesten und schnellsten Javascript-basierten Bibliotheken. Besuchen Sie es hier: https://hammerjs.github.io/
Möglicherweise fällt es Ihnen leichter, es zuerst mit Mausereignissen zum Prototypen zu implementieren.
Hier gibt es viele Antworten, einschließlich der oberen, die mit Vorsicht verwendet werden sollten, da sie Edge-Fälle, insbesondere um Begrenzungsrahmen, nicht berücksichtigen.
Sehen:
Sie müssen experimentieren, um Randfälle und Verhaltensweisen wie den Zeiger, der sich außerhalb des Elements befindet, vor dem Beenden zu erfassen.
Ein Swipe ist eine sehr grundlegende Geste, die eine höhere Ebene der Interaktionsverarbeitung von Schnittstellenzeigern darstellt, die ungefähr zwischen der Verarbeitung von Rohereignissen und der Handschrifterkennung liegt.
Es gibt keine einzige exakte Methode zum Erkennen eines Wischens oder Schleuderns, obwohl im Allgemeinen alle einem Grundprinzip zum Erkennen einer Bewegung über ein Element mit einer Entfernungs-, Geschwindigkeits- oder Geschwindigkeitsschwelle folgen. Man könnte einfach sagen, wenn innerhalb einer bestimmten Zeit eine Bewegung über 65% der Bildschirmgröße in eine bestimmte Richtung erfolgt, ist dies ein Schlag. Wo genau Sie die Linie zeichnen und wie Sie sie berechnen, liegt ganz bei Ihnen.
Einige betrachten es möglicherweise auch aus der Perspektive des Impulses in eine Richtung und wie weit es vom Bildschirm weggeschoben wurde, als das Element losgelassen wurde. Dies ist klarer mit klebrigen Wischen, 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 zu versuchen, eine Gestenbibliothek zu finden, die Sie entweder portieren oder wiederverwenden können, um die Konsistenz zu gewährleisten. Viele der hier aufgeführten Beispiele sind zu simpel und registrieren einen Schlag als die geringste Berührung in eine beliebige Richtung.
Android wäre die naheliegende Wahl, hat aber das gegenteilige Problem, es ist zu komplex.
Viele Leute scheinen die Frage als irgendeine 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 gebogen sein kann und bestimmte Beschleunigungseigenschaften hat). Ähnlich verhält es sich mit einem Fling, der jedoch beabsichtigt, einen Gegenstand aus eigener Kraft ein Stück weit wegzutreiben.
Die beiden sind 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 (den physischen Bildschirm wischen, aber das auf dem Bildschirm angezeigte UI-Element bewegen).
Die beste Option ist, es nicht selbst zu tun. Es gibt bereits eine große Anzahl von JavaScript-Bibliotheken zum Erkennen einfacher Gesten .
Einfaches Vanilla JS-Beispiel für horizontales Streichen:
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 die gleiche Logik für vertikales Streichen verwenden.
Ein Beispiel für die Verwendung von 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]
}
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.
Wenn Sie nur Wischen benötigen, ist die Größe besser, wenn Sie nur das benötigte Teil verwenden. Dies sollte auf jedem Touchgerät funktionieren.
Dies sind ~ 450 Bytes nach gzip-Komprimierung, Minification, Babel usw.
Ich habe die unten stehende Klasse basierend auf den anderen Antworten geschrieben, sie verwendet einen Prozentsatz, der anstelle von Pixeln verschoben wurde, und ein Event-Dispatcher-Muster, um Dinge zu aktivieren/deaktivieren.
Verwenden Sie es wie folgt:
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;