Detectar um furto de dedo através de JavaScript no iPhone e Android

268

Como você pode detectar que um usuário passou o dedo em alguma direção em uma página da Web com JavaScript?

Eu queria saber se havia uma solução que funcionaria para sites no iPhone e um telefone Android.

827
fonte
1
Para reconhecimento de furto, eu recomendaria o Hammer.js . É bastante pequeno, e suporta muitos gestos: - Swipe - Girar - Pitada - Press (longa espera) - Tap - Pan
Will Brickner
Há um evento: "touchmove"
Clay
@ Argila que ainda não funciona no Safari, portanto, não há iPhone.
Jakuje 12/06

Respostas:

342

Exemplo simples de código JS baunilha:

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

Testado no Android.

givanse
fonte
1
Parece legal e simples, alguma idéia do que é o suporte para estes eventos touchstart, touchmove?
precisa saber é
1
Funciona muito bem, mas tem um problema ao detectar movimentos retos. Vou postar outra resposta neste tópico que corrigiu isso como solução JQuery (desktop). Ele também adiciona a versão do mouse desses eventos de furto e adiciona uma opção de sensibilidade.
CódigoBeat
1
Droga. O tópico está fechado, portanto não é possível adicionar minha resposta!
Codebeat
3
Isso funciona muito bem, mas esquerda / direita e cima / baixo estão ao contrário.
Peter Eisentraut
4
originalEvent é uma propriedade JQuery. Deve ficar de fora se você executar o javascript puro sem o JQuery. O código atual gera uma exceção se executado sem JQuery.
Jan Derk 21/05
31

Com base na resposta de @ givanse, é assim que você pode fazer isso com 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);
    }
}

Você pode usá-lo assim:

// 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
fonte
7
esse código provavelmente não funcionará porque você receberá uma exceção ao tentar chamar .bindde indefinido porque handleTouchMovena verdade não retornou nada. Também é inútil para chamar ligam ao chamar função com this.porque ele já é obrigado a actual contexto
nick.skriabin
3
Acabei de remover .bind(this);e funcionou normalmente. Obrigado @nicholas_r
Ali Ghanavatian
Parte pegue o elemento você mesmo, basta remover '#' em document.getElementById ('my-element') e funcionou bem. Obrigado @Marwelln :)
Blue Tram
4
Se você quiser esperar até o furto FINALIZAR (ou seja, depois que eles levantarem o dedo ou se acostumarem), mude touches[0]para changedTouches[0]e o tipo de manipulador de eventos handleTouchMoveparahandleTouchEnd
TetraDev 19/04/19
chamar run()duas vezes e você terá um vazamento de memória desagradável
Mat
20

Mesclei algumas das respostas aqui em um script que usa o CustomEvent para disparar eventos com swip no DOM. Adicione o script swiped-events.min.js de 0,7k à sua página e ouça eventos swiped :

deslizou para a esquerda

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

deslizou para a direita

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

roubado

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

deslocado

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

Você também pode anexar diretamente a um elemento:

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

Configuração opcional

Você pode especificar os seguintes atributos para ajustar como a interação de furto funciona na sua página (esses são opcionais) .

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

O código fonte está disponível no Github

John Doherty
fonte
Eu vim aqui porque puro-furto não estava funcionando para mim em MOBILE
StefanBob
@StefanBob se você levantar um carrapato na repo github com informações suficientes para me permitir reproduzir o problema, eu vou olhar para ele
John Doherty
1
Obrigado, funciona perfeitamente! Substituí o Hammer.js pela sua biblioteca, porque a primeira não funciona com o zoom do navegador e isso é um sério problema de usabilidade. Com esta biblioteca o zoom funciona corretamente (testado em Android)
collimarco
15

o que eu usei antes é que você precisa detectar o evento de redução do mouse, registrar sua localização x, y (o que for relevante), detectar o evento de mouseup e subtrair os dois valores.

helloandre
fonte
28
Acredito que é o touchstart, o touchmove, o touchcancel e o touchend com os quais se trabalharia, e não o mouse ou a ratoeira.
Volomike
12

Descobri que a resposta brilhante da @givanse é a mais confiável e compatível em vários navegadores móveis para registrar ações de furto.

No entanto, é necessário alterar seu código para fazê-lo funcionar nos navegadores móveis modernos que estão sendo usados jQuery.

event.touchesnão existirá se jQueryfor usado e resultar undefinede deve ser substituído por event.originalEvent.touches. Sem jQuery, event.touchesdeve funcionar bem.

Então a solução se torna,

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

Testado em:

  • Android : Chrome, navegador UC
  • iOS : Safari, Chrome, navegador UC
nashcheez
fonte
originalEvent é uma propriedade JQuery. Ele nem existe em Javascript puro.
Jan Derk
1
De acordo com essa resposta do SO , um evento de toque, se suportado pelo navegador, será exposto event.originalEvent. A coisa é event.touchesdeixou de existir agora e resulta em undefined.
Nashcheez 21/05/19
event.touches apenas deixaram de existir ao usar o JQuery. Experimente o seu código sem o JQuery e você receberá um erro que evt.originalEvent é indefinido. O JQuery substitui totalmente o evento pelo seu próprio e coloca o evento do navegador nativo no evento original. Versão curta: seu código funciona apenas com o JQuery. Funciona sem o JQuery se você remover o evento original.
Jan Derk
1
Sim, pesquisei um pouco e percebi que você estava certo sobre a disponibilidade da ativação do jquery event.originalEvent. Vou atualizar minha resposta. Obrigado! :)
nashcheez
6

Algum mod de resposta mais animada (não posso comentar ...) para lidar com furtos curtos

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

lixeira, furto de tempo limite, 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;
}
Sergey Guns
fonte
4

Se alguém estiver tentando usar o jQuery Mobile no Android e tiver problemas com a detecção de furto JQM

(Eu tinha alguns no Xperia Z1, Galaxy S3, Nexus 4 e alguns telefones Wiko também) isso pode ser útil:

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

O furto no android não foi detectado, a menos que fosse um furto muito longo, preciso e rápido.

Com essas duas linhas, funciona corretamente

Rayjax
fonte
Eu também precisava acrescentar: $.event.special.swipe.scrollSupressionThreshold = 8;mas você me colocou na direção certa! Obrigado!
Philip G
4

Eu tive problemas com o manipulador de touchend disparando continuamente enquanto o usuário arrastava um dedo. Não sei se isso é devido a algo que estou fazendo de errado ou não, mas religuei isso para acumular movimentos com touchmove e touchend realmente aciona o retorno de chamada.

Eu também precisava ter um grande número dessas instâncias e, portanto, adicionei os métodos de habilitação / desabilitação.

E um limite em que um golpe curto não é acionado. O toque inicial zero é os contadores de cada vez.

Você pode alterar o target_node em tempo real. Ativar na criação é opcional.

/** 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);
            }
        }
    }
}
Toalhas na moda
fonte
Isso é para ES5 ou ES6?
1,21 gigawatts
@gigawatts Não me lembro. O projeto usado que alcançou a EOL já e eu não precisei do código desde então. Eu suspeito que eu estava escrevendo para o ES6, mas isso foi há mais de 2 anos.
Trendal Toews
3

Também mesclei algumas respostas, principalmente a primeira e a segunda com classes, e aqui está minha versão:

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

Posteriormente pode usá-lo da seguinte maneira:

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

Isso ajuda a evitar erros de console quando você deseja chamar apenas, digamos, o método "onLeft".

Romanna Semenyshyn
fonte
2

Se você só precisa deslizar, é melhor usar o tamanho apenas usando a peça que precisa. Isso deve funcionar em qualquer dispositivo de toque.

Isso é ~ 450 bytes 'após a compressão gzip, minificação, babel etc.

Eu escrevi a classe abaixo com base nas outras respostas, ela usa a porcentagem movida em vez de pixels e um padrão de despachante de evento para prender / soltar as coisas.

Use-o assim:

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

Eu queria detectar apenas o furto esquerdo e direito, mas acionar a ação somente quando o evento de toque terminar , então modifiquei levemente a ótima resposta do @ givanse para fazer isso.

Por que fazer isso? Se, por exemplo, enquanto desliza, o usuário percebe que ele finalmente não quer deslizar, ele pode mover o dedo na posição original (um aplicativo de telefone "namoro" muito popular faz isso;)) e, em seguida, o "deslizar para a direita" evento é cancelado.

Portanto, para evitar um evento "deslize para a direita" apenas porque há uma diferença de 3px horizontalmente, adicionei um limite no qual um evento é descartado: para ter um evento "deslize para a direita", o usuário deve deslizar pelo menos 1/3 da largura do navegador (é claro que você pode modificar isso).

Todos esses pequenos detalhes aprimoram a experiência do usuário. Aqui está o código (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;
}
Basj
fonte
1

Exemplo simples de baunilha JS para deslizar horizontal:

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

Você pode usar a mesma lógica para deslizar verticalmente.

Damjan Pavlica
fonte
1

Adicionando a esta resposta aqui . Este adiciona suporte para eventos do mouse para teste na área de trabalho:

<!--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 gigawatts
fonte
1

Eu reformulei a solução da @givanse para funcionar como um gancho do React. A entrada é alguns ouvintes de eventos opcionais, a saída é uma ref funcional (precisa ser funcional para que o gancho possa executar novamente quando / se a ref for alterada).

Também incluído nos parâmetros de limiar de furto vertical / horizontal, para que pequenos movimentos não acionem acidentalmente os ouvintes do evento, mas eles podem ser configurados como 0 para imitar mais de perto a resposta original.

Dica: para obter o melhor desempenho, as funções de entrada do ouvinte de eventos devem ser memorizadas.

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.
fonte
0

Um exemplo de como usar com deslocamento.

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

Илья Зеленько
fonte
Atualmente usando esta versão. Como impedir que isso seja disparado várias vezes se for repetido? Utilizo isso com o recurso animar para um formulário de rolagem lateral e quando deslizo várias vezes, as coisas ficam um pouco confusas e meus divs começam a se sobrepor na área visível.
NMALM
0

Você pode ter mais facilidade para implementá-lo primeiro com eventos de mouse para criar protótipo.

Há muitas respostas aqui, incluindo a parte superior, que devem ser usadas com cautela, pois não consideram casos de arestas, especialmente em torno de caixas delimitadoras.

Vejo:

Você precisará experimentar os casos e comportamentos de ponta, como o ponteiro se movendo para fora do elemento antes de terminar.

Um furto é um gesto muito básico, que é um nível mais alto de processamento da interação do ponteiro da interface, aproximadamente entre o processamento de eventos brutos e o reconhecimento de manuscrito.

Não existe um método exato para detectar um golpe ou arremesso, embora praticamente todos geralmente sigam um princípio básico de detectar um movimento através de um elemento com um limite de distância e velocidade ou velocidade. Você pode simplesmente dizer que, se houver um movimento em 65% do tamanho da tela em uma determinada direção dentro de um determinado período de tempo, é um golpe. Exatamente onde você desenha a linha e como calcula, depende de você.

Alguns também podem vê-lo da perspectiva do momento em uma direção e a que distância da tela ele foi pressionado quando o elemento foi liberado. Isso fica mais claro com furtos pegajosos, nos quais o elemento pode ser arrastado e, ao ser liberado, se recupera ou voa para fora da tela, como se o elástico quebrasse.

Provavelmente, é ideal tentar encontrar uma biblioteca de gestos que você possa portar ou reutilizar, normalmente usada para obter consistência. Muitos dos exemplos aqui são excessivamente simplistas, registrando um furto como o menor toque em qualquer direção.

O Android seria a escolha óbvia, embora tenha o problema oposto, é excessivamente complexo.

Muitas pessoas parecem ter interpretado mal a pergunta como qualquer movimento em uma direção. Um furto é um movimento amplo e relativamente breve em uma única direção (embora possa ser arqueado e ter certas propriedades de aceleração). Uma aventura é semelhante, embora pretenda impulsionar casualmente um item a uma distância razoável, sob seu próprio momento.

Os dois são suficientemente semelhantes que algumas bibliotecas podem fornecer apenas arremessar ou deslizar, o que pode ser usado de forma intercambiável. Em uma tela plana, é difícil separar verdadeiramente os dois gestos e, de maneira geral, as pessoas estão fazendo os dois (passando a tela física, mas jogando o elemento da interface do usuário exibido na tela).

Sua melhor opção é não fazer você mesmo. Já existe um grande número de bibliotecas JavaScript para detectar gestos simples .

jgmjgm
fonte