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.
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.
Risposte:
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.
touchstart
, touchmove
?
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();
.bind
indefinito perché in handleTouchMove
realtà non hai restituito nulla. inoltre è inutile chiamare bind quando si chiama la funzione this.
perché è già legato al contesto corrente
.bind(this);
e ha funzionato con grazia. grazie @nicholas_r
touches[0]
a changedTouches[0]
e il tipo di gestore eventi handleTouchMove
suhandleTouchEnd
run()
due volte e hai una brutta perdita di memoria
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 :
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
});
Puoi anche collegarti direttamente a un elemento:
document.getElementById('myBox').addEventListener('swiped-down', function(e) {
console.log(e.target); // the element that was swiped
});
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
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.
jQuery Mobile include anche il supporto per lo scorrimento: http://api.jquerymobile.com/swipe/
Esempio
$("#divId").on("swipe", function(event) {
alert("It's a swipe!");
});
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.touches
non esiste se jQuery
viene utilizzato e risulta undefined
e deve essere sostituito da event.originalEvent.touches
. Senza jQuery
, event.touches
dovrebbe 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:
event.originalEvent
. La cosa è event.touches
cessata di esistere ora e risulta undefined
.
event.originalEvent
. Aggiornerò la mia risposta. Grazie! :)
Ho riconfezionato TouchWipe
come un breve plugin jquery:detectSwipe
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;
}
};
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;
}
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
$.event.special.swipe.scrollSupressionThreshold = 8;
ma mi hai messo nella giusta direzione! Grazie!
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);
}
}
}
}
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".
Usato due:
jQuery mobile: funziona nella maggior parte dei casi e specialmente quando stai sviluppando un'applicazione che utilizza altri plugin jQuery, quindi meglio usare i controlli mobili jQuery per questo. Visita qui: https://www.w3schools.com/jquerymobile/jquerymobile_events_touch.asp
Hammer Time! una delle librerie javascript migliori, leggere e veloci. Visita qui: https://hammerjs.github.io/
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;
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;
}
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.
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!') })
});
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);
};
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]
}
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 .