Jak wykryć, że użytkownik przesunął palcem w jakimś kierunku po stronie internetowej z JavaScript?
Zastanawiałem się, czy istnieje jedno rozwiązanie, które działałoby na stronach internetowych zarówno na iPhonie, jak i telefonie z Androidem.
Jak wykryć, że użytkownik przesunął palcem w jakimś kierunku po stronie internetowej z JavaScript?
Zastanawiałem się, czy istnieje jedno rozwiązanie, które działałoby na stronach internetowych zarówno na iPhonie, jak i telefonie z Androidem.
Odpowiedzi:
Prosty przykładowy waniliowy kod JS:
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;
};
Testowane w systemie Android.
touchstart
, touchmove
?
Na podstawie odpowiedzi @ givanse możesz to zrobić za pomocą 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);
}
}
Możesz go użyć w następujący sposób:
// 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();
.bind
niezdefiniowanego, ponieważ w handleTouchMove
rzeczywistości nic nie zwróciłeś. również bezużyteczne jest wywoływanie wiązania podczas wywoływania funkcji, this.
ponieważ jest już związane z bieżącym kontekstem
.bind(this);
i zadziałało z wdziękiem. dziękuję @nicholas_r
touches[0]
na changedTouches[0]
i typ obsługi zdarzenia handleTouchMove
nahandleTouchEnd
run()
dwa razy, a pojawi się nieprzyjemny wyciek pamięci
Połączyłem kilka odpowiedzi tutaj w skrypcie, który używa CustomEvent do uruchamiania zdarzeń przeciągnięcia w DOM. Dodaj 0.7k przeciągnął-events.min.js skrypt do swojej strony i nasłuchiwać przeciągnął zdarzeń:
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
});
Możesz także dołączyć bezpośrednio do elementu:
document.getElementById('myBox').addEventListener('swiped-down', function(e) {
console.log(e.target); // the element that was swiped
});
Możesz określić następujące atrybuty, aby dostosować funkcjonowanie interakcji przeciągania na stronie (są one opcjonalne) .
<div data-swipe-threshold="10"
data-swipe-timeout="1000"
data-swipe-ignore="false">
Swiper, get swiping!
</div>
Kod źródłowy jest dostępny na Github
to, czego użyłem wcześniej, to wykrycie zdarzenia mousedown, zarejestrowanie jego lokalizacji x, y (w zależności od tego, co jest istotne), a następnie wykrycie zdarzenia mysi i odjęcie dwóch wartości.
jQuery Mobile obejmuje również obsługę przesuwania: http://api.jquerymobile.com/swipe/
Przykład
$("#divId").on("swipe", function(event) {
alert("It's a swipe!");
});
Uważam, że świetna odpowiedź @givanse jest najbardziej niezawodna i kompatybilna z wieloma przeglądarkami mobilnymi do rejestrowania operacji przeciągania.
Jednak jego kod wymaga zmiany, aby działał we współczesnych przeglądarkach mobilnych, które używają jQuery
.
event.touches
nie będzie istniał, jeśli jQuery
zostanie użyty, a wyniki undefined
zostaną zastąpione przez event.originalEvent.touches
. Bez jQuery
, event.touches
powinny działać prawidłowo.
Rozwiązaniem staje się
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;
};
Testowane na:
event.originalEvent
. Rzecz event.touches
przestała istnieć teraz i skutkuje undefined
.
event.originalEvent
. Zaktualizuję swoją odpowiedź. Dzięki! :)
Przepakowałem TouchWipe
jako krótką wtyczkę jquery:detectSwipe
Jakiś mod najwyższej odpowiedzi (nie może komentować ...), aby poradzić sobie z krótkimi przeciągnięciami
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;
}
};
kosz, przeciągnięcie limitu czasu, dodawanie swipeBlockElems.
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;
}
Jeśli ktoś próbuje używać jQuery Mobile na Androidzie i ma problemy z wykrywaniem przesuwania JQM
(Miałem trochę na Xperia Z1, Galaxy S3, Nexus 4 i niektóre telefony Wiko). Może to być przydatne:
//Fix swipe gesture on android
if(android){ //Your own device detection here
$.event.special.swipe.verticalDistanceThreshold = 500
$.event.special.swipe.horizontalDistanceThreshold = 10
}
Przesunięcie na Androidzie nie zostało wykryte, chyba że było to bardzo długie, precyzyjne i szybkie przeciągnięcie.
Z tymi dwiema liniami działa poprawnie
$.event.special.swipe.scrollSupressionThreshold = 8;
ale skierowałeś mnie w dobrym kierunku! Dziękuję Ci!
Miałem problem z ciągłym ostrzeżeniem, gdy użytkownik przeciągał palcem po okolicy. Nie wiem, czy to z powodu czegoś, co robię źle, czy nie, ale ponownie to przełączyłem, aby gromadzić ruchy za pomocą touchmove, a touchend faktycznie wyzwala oddzwonienie.
Musiałem także mieć dużą liczbę tych instancji, dlatego dodałem metody włączania / wyłączania.
I próg, w którym krótkie machnięcie nie strzela. Touchstart zero za każdym razem liczniki.
Możesz zmienić węzeł target_node w locie. Włącz przy tworzeniu jest opcjonalny.
/** 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);
}
}
}
}
Połączyłem też kilka odpowiedzi, głównie pierwszą i drugą z klasami, a oto moja wersja:
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;
}
}
Następnie można użyć go w następujący sposób:
let swiper = new Swipe({
onLeft() {
console.log('You swiped left.');
}
});
Pomaga uniknąć błędów konsoli, gdy chcesz wywoływać tylko powiedzmy metodę „onLeft”.
Używane dwa:
jQuery mobile: działa w większości przypadków, a zwłaszcza gdy tworzysz aplikację, która korzysta z innej wtyczki jQuery, lepiej jest do tego użyć kontrolek jQuery mobile. Odwiedź go tutaj: https://www.w3schools.com/jquerymobile/jquerymobile_events_touch.asp
Czas młota ! jedna z najlepszych, lekkich i szybkich bibliotek opartych na javascript. Odwiedź go tutaj: https://hammerjs.github.io/
Jeśli potrzebujesz tylko machnięcia, lepiej jest użyć mniejszego rozmiaru, używając tylko potrzebnej części. Powinno to działać na dowolnym urządzeniu dotykowym.
Jest to ~ 450 bajtów po kompresji gzip, minimalizacji, babel itp.
Napisałem niższą klasę w oparciu o inne odpowiedzi, używa ona przesunięcia procentowego zamiast pikseli oraz wzorca dyspozytora zdarzeń do zaczepiania / odczepiania rzeczy.
Używaj go w ten sposób:
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;
Chciałem, aby wykryć tylko lewy i prawy przesunąć, ale wywołać działania tylko wtedy, gdy zdarzenie dotykowy końce , więc lekko zmodyfikowany wielką odpowiedź znaku @ givanse aby to zrobić.
Po co to robić? Jeśli na przykład podczas przeciągania użytkownik zauważy, że w końcu nie chce przesunąć, może przesunąć palec w pierwotnej pozycji (robi to bardzo popularna aplikacja do randek;)), a następnie „przesuń w prawo” wydarzenie zostało anulowane.
Aby więc uniknąć zdarzenia „przeciągnij w prawo” tylko dlatego, że istnieje różnica 3 pikseli w poziomie, dodałem próg, poniżej którego zdarzenie jest odrzucane: aby mieć zdarzenie „przeciągnięcie w prawo”, użytkownik musi przesunąć przynajmniej 1/3 szerokości przeglądarki (oczywiście możesz to zmienić).
Wszystkie te małe szczegóły zwiększają komfort użytkowania. Oto kod (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;
}
Prosty waniliowy przykład JS dla przesunięcia poziomego:
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()
})
Możesz użyć tej samej logiki do pionowego przeciągnięcia.
Dodanie do tej odpowiedzi tutaj . Ten dodaje obsługę zdarzeń myszy do testowania na komputerze:
<!--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!') })
});
Przykład użycia z przesunięciem.
// 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]
}
Być może łatwiej będzie ci najpierw zaimplementować go za pomocą zdarzeń myszy do prototypowania.
Jest tu wiele odpowiedzi, w tym górna, należy zachować ostrożność, ponieważ nie uwzględniają przypadków krawędzi, szczególnie wokół obwiedni.
Widzieć:
Będziesz musiał eksperymentować, aby uchwycić przypadki krawędzi i zachowania, takie jak wskaźnik poruszający się poza elementem przed zakończeniem.
Przesunięcie jest bardzo podstawowym gestem, który stanowi wyższy poziom przetwarzania interakcji interfejsu wskaźnikowego z grubsza siedzącego między przetwarzaniem nieprzetworzonych zdarzeń a rozpoznawaniem pisma ręcznego.
Nie ma jednej dokładnej metody wykrywania przesunięcia lub rzutu, chociaż praktycznie wszystkie zasadniczo przestrzegają podstawowej zasady wykrywania ruchu w poprzek elementu o progu odległości i prędkości lub prędkości. Można po prostu powiedzieć, że jeśli ruch w 65% rozmiaru ekranu w danym kierunku następuje w danym czasie, jest to przesunięcie. To, gdzie narysujesz linię i jak ją obliczysz, zależy od ciebie.
Niektórzy mogą również patrzeć na to z perspektywy pędu w kierunku i jak daleko od ekranu został wypchnięty, gdy element zostanie zwolniony. Jest to wyraźniejsze dzięki lepkim przeciągnięciom, w których element można przeciągnąć, a następnie podczas zwalniania albo odbije się do tyłu, albo odleci z ekranu, jakby sprężysta pękła.
Prawdopodobnie idealnie jest znaleźć bibliotekę gestów, którą można przenieść lub użyć ponownie, która jest zwykle używana w celu zachowania spójności. Wiele przykładów tutaj jest nadmiernie uproszczonych, rejestrując machnięcie jako najmniejszy dotyk w dowolnym kierunku.
Android byłby oczywistym wyborem, choć ma odwrotny problem, jest zbyt skomplikowany.
Wydaje się, że wiele osób błędnie zinterpretowało to pytanie jako każdy ruch w danym kierunku. Przeciągnięcie to szeroki i stosunkowo krótki ruch w przeważającej części w jednym kierunku (choć może być łukowy i mieć pewne właściwości przyspieszające). Rzut jest podobny, choć ma zamiar niedbale wypchnąć przedmiot na znaczną odległość pod własnym pędem.
Te dwie są wystarczająco podobne, że niektóre biblioteki mogą zapewniać tylko fling lub machnięcie, których można używać zamiennie. Na płaskim ekranie trudno jest naprawdę rozdzielić dwa gesty i ogólnie mówiąc, ludzie robią oba (przesuwając ekran fizyczny, ale odrzucając element interfejsu użytkownika wyświetlany na ekranie).
Najlepszą opcją jest nie robić tego sam. Istnieje już duża liczba bibliotek JavaScript do wykrywania prostych gestów .
I przerobione @givanse rozwiązanie jest do działania jako React haka. Dane wejściowe to niektóre opcjonalne detektory zdarzeń, dane wyjściowe to funkcjonalny ref (musi być funkcjonalny, aby można było ponownie uruchomić hak, gdy / jeśli ref zmieni).
Dodano również parametr progu przesunięcia w pionie / poziomie, aby małe ruchy nie wyzwalały przypadkowo detektorów zdarzeń, ale można je ustawić na 0, aby dokładniej naśladować oryginalną odpowiedź.
Wskazówka: aby uzyskać najlepszą wydajność, funkcje wejściowe detektora zdarzeń powinny zostać zapamiętane.
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);
};